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과 동일한 결과값이 나오지 않는 문제가 발생합니다)
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); // 한 건씩 업데이트 되었음을 확인할 수 있음
}
}
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)에서 예상하지 못한 문제를 발생시킬 가능성이 있습니다.
mariadb-connector-j
JIRA에서 위와 관련된 이슈를 확인해보았습니다.사실 connector의 2.x 버전대에서 이미 위의 이슈가 보고된 적이 있으며,
원인은 Batch 작업을 빠르게 하기위해 useBulkStmts=true
옵션이 기본적으로 활성화되도록 업데이트 되면서 생긴 이슈였습니다.
위 이슈가 보고된 이후, 위의 옵션의 기본값을 true
로 하지않고 false
로 하도록 패치가 되었고 그렇게 이슈가 마무리 되었습니다.
하지만, 현 시점에서 최신버전인 3.x 대에서 다시 해당옵션의 기본값이 true
로 변경이 되었고,
이로인해 사용자들이 해당 문제를 이슈로 제기하였습니다.
MariaDB Connector를 개발한 메인 개발자들은 다시 제기된 이슈를 통해 문제를 확인하였지만,
MariaDB-Server 에서의 수정이 필요한 부분으로 보고 대응을 하고 있는 것으로 보입니다.
MariaDB 쪽에서 해당 수정요청이 반영이 될지는 아직 모르지만,
반영되기 전까지는 옵션을 수동으로 조정하라고 가이드하고 있습니다.
org.mariadb.jdbc.Configuration.useBulkStmts
위의 세팅을 수동으로 false로 변경하는 작업을 수동으로 해줘야 합니다.
간단한 방법으로는 DB 연결시 url에 명시적으로 옵션을 추가하여 조정하는 방법이 있습니다.
url: jdbc:log4jdbc:mariadb://localhost:3306/testdb?characterEncoding=UTF-8&serverTimezone=UTC&allowMultiQueries=true&useBulkStmts=false
만약 mariadb-connector-j
를 사용하고 있을 때, 배치 쿼리의 각각의 실행결과가 필요하다면,
useBulkStmts
옵션값을 false 수동설정하여야 합니다.
이는 대량의 배치 실행시, 약간의 성능하락이 발생할 수도 있습니다.