[이슈 공유] MariaDB Executor.BATCH 시, BatchResult#getUpdateCounts() return 값 이슈

이상현·2023년 3월 26일
0

차곡차곡

목록 보기
8/8
post-thumbnail

들어가며

MariaDB는 Mysql의 대체 DB로 많이 사용되고 있습니다.
실제로 MySql과 많은 부분 동일하게 동작하며, mysql에 연결하기 위한 jdbc 드라이버로 mariadb-driver를 사용해도 대부분 문제없이 동작합니다.

이 글에서 설명하는 이슈는 현재기준 최신버전 (MariaDB Connector/Java 3.1.3) 에서 발생하는 이슈로
해당드라이버를 사용하는 프로젝트에서 문제가 발생할 수 있음을 공유합니다.

문제

MariaDB를 사용할 때 사용되는 mariadb-connector-j 의존성에서 발생하는 이슈로
대량의 데이터를 한번에 처리하기 위한 Batch(또는 Bulk) 작업 시, ExecutorType.BATCH 모드로 SqsSession을 사용하면
INSERT/UPDATE의 결과값(리턴값)으로 AffectedRows가 정상적으로 리턴되지 않는 문제가 발생합니다.
(정확히는 Mysql과 동일한 결과값이 나오지 않는 문제가 발생합니다)


상황

정상적으로 동작하는 경우

  • Mysql + mysql-connector-j (mysql용 jdbc) 일 때의 테스트 코드입니다.
  • SqlSession의 배치작업의 경우, flushStatements() 리턴된 값을 통해, 실행된 쿼리들의 affectedRow를 확인할 수 있습니다.
@Test
@DisplayName("SqlSession Execute.BATCH 를 이용한 배치처리")
void BulkUpdateUsingSqlSession(){

      SqlSession sqlSession=sqlSessionFactory.openSession(ExecutorType.BATCH);  // 배치실행모드로 SqlSession을 생성
      ItemMapper mapper=sqlSession.getMapper(ItemMapper.class);

      int retValue=0;

      for(ItemDto itemDto:inputDataList){
      itemDto.setPrice(itemDto.getPrice()*10);  // item의 가격을 10배 인상한다고 가정
      retValue=mapper.updateItem(itemDto);  // 배치실행모드의 경우, 리턴값으로 row와 상관없는 값이 나옴
      }
      List<BatchResult> batchResults=sqlSession.flushStatements();   // 배치실행 모드에서는 flushStatements을 이용하여 배치의 결과를 담은 리스트를 얻을 수 있음.

      int[]updateCounts=batchResults.get(0).getUpdateCounts();  // batchResults를 통해, 실행된 쿼리의 AffectedRow를 확인할 수 있음
      for(int updateCnt:updateCounts){      // [1,1,1,1,...]
      Assert.assertEquals(updateCnt, 1);    // 한 건씩 업데이트 되었음을 확인할 수 있음 
      }
  }
  

이슈 상황

  • MariaDB(또는 Mysql) + mariadb-connector-j (mariaDB용 jdbc)를 사용할 때
  • flushStatements()에서의 리턴값으로 Mysql과 다르게 동작함
@Test
@DisplayName("SqlSession Execute.BATCH 를 이용한 배치처리")
void BulkUpdateUsingSqlSession(){

    (...)
    
    int[]updateCounts=batchResults.get(0).getUpdateCounts();  // batchResults를 통해, 실행된 쿼리의 AffectedRow를 확인할 수 있음
    for(int updateCnt:updateCounts){      // [-2,-2,-2,-2,...]
    Assert.assertEquals(updateCnt, 1);    // **테스트 실패**. 음수의 결과값이 나오는 것을 확인
    }
}
  • 위 코드를 실행해보면, UpdateCnt의 값으로 -2 가 리턴됩니다.
    이 값은 MariaDB에서 SUCCESS_NO_INFO로 아래와 같이 정의되어 있습니다.

  • 실제로 반영된 row의 수가 리턴되지 않는 이유는, mariadb-connector-j에서 내부적으로 Batch 작업의 효율적인 처리를 위함으로 보입니다.

  • 하지만 이러한 차이로 인해, mysql DB와 동일하게 동작할거라 생각하고 사용한 타 프로젝트(eg. mybatis)에서 예상하지 못한 문제를 발생시킬 가능성이 있습니다.


이슈 모니터링 상황

사실 connector의 2.x 버전대에서 이미 위의 이슈가 보고된 적이 있으며,
원인은 Batch 작업을 빠르게 하기위해 useBulkStmts=true 옵션이 기본적으로 활성화되도록 업데이트 되면서 생긴 이슈였습니다.

위 이슈가 보고된 이후, 위의 옵션의 기본값을 true로 하지않고 false로 하도록 패치가 되었고 그렇게 이슈가 마무리 되었습니다.

하지만, 현 시점에서 최신버전인 3.x 대에서 다시 해당옵션의 기본값이 true로 변경이 되었고,
이로인해 사용자들이 해당 문제를 이슈로 제기하였습니다.

MariaDB Connector를 개발한 메인 개발자들은 다시 제기된 이슈를 통해 문제를 확인하였지만,
MariaDB-Server 에서의 수정이 필요한 부분으로 보고 대응을 하고 있는 것으로 보입니다.

MariaDB 쪽에서 해당 수정요청이 반영이 될지는 아직 모르지만,
반영되기 전까지는 옵션을 수동으로 조정하라고 가이드하고 있습니다.


조치방법

  • org.mariadb.jdbc.Configuration.useBulkStmts

위의 세팅을 수동으로 false로 변경하는 작업을 수동으로 해줘야 합니다.

간단한 방법으로는 DB 연결시 url에 명시적으로 옵션을 추가하여 조정하는 방법이 있습니다.

  • DB 연결과 관련된 설정에서 아래와 같이 수정 (useBulkStmts=false)
     url: jdbc:log4jdbc:mariadb://localhost:3306/testdb?characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true&useBulkStmts=false

정리

만약 mariadb-connector-j 를 사용하고 있을 때, 배치 쿼리의 각각의 실행결과가 필요하다면,
useBulkStmts 옵션값을 false 수동설정하여야 합니다.

이는 대량의 배치 실행시, 약간의 성능하락이 발생할 수도 있습니다.

profile
'당신을 한 줄로 소개해보세요'를 이 블로그로 대신 해볼까합니다.

0개의 댓글