본문 바로가기

Programing/Framework

[JOOQ] MySQL JDBC batch 벤치마킹

batch 처리에 대한 의견

코드리뷰를 하다 jdbc의 batch 처리를 하면 성능 효과를 얻을 수 있을 것 같다는 성준님의 의견이 있었다.

executeBatch 를 이용한 방법입니다. (e.g Batch Processing in JDBC atBaeldung)
단일 DELETE N 건 실행 대비, 성능상 이점이 있습니다.

평소 성준님은 최적화에 대해 M.A.Jackson의 말을 인용하시던 분이었다.

최적화를 할 때는 아래의 두 규칙을 따르라.
규칙1: 하지마라
규칙2: (전문가들만 따를 것) 아직은 하지마라 - 완벽히 명료한, 최적화되지 않은 해답을 얻을 때까지는.
- M. A. 잭슨(M. A. Jackson)

사실 네트워크 round-time을 생각해본다면 상식적으로 한번에 batch로 보내는 것이 좋을 것이라는 직감이 들었다.
하지만 그것이 얼마나 효과적으로 좋아질 것인지에 대해 궁금해져서 벤치마킹을 하게 되었다.

사례 연구

저녁을 먹으면서 JDBC를 이용한 Batch Updates의 성능 고찰이라는 글을 봤다.
해당 블로그에는 Vlad Mihalcea의 High-Performance java Persistence의 책이 인용되어 있었다.

MySQL의 경우 rewriteBatchedStatements 스위치를 켜야 서버 사이드의 prepared statements들을 처리할 수 있다고 한다.

나의 경우 최대한 배치 크기가 1000이라 위의 표의 정보 범위 내에 있었다.

 

jOOQ 역시 배치처리에 대한 메서드들을 제공하고 있었다.

www.jooq.org/doc/3.13/manual/sql-execution/batch-execution/

 

또한 어제 퇴근을 하려는데  사내 DBA님께 위의 내용을 여쭈어보았다.
처음에는 본인도 별로 성능 차이가 없을 것 같았는데 막상 실제 2배 이상의 월등히 배치 처리가 성능이 높아서 적용을 했었다고 한다.

 

일단 개선에 대한 효과가 긍정적일 가능성은 높아 보였다.

하지만 레이건 대통령이 무기 감축을 협정할 때 러시아의 옛 속담을 차용해서 했던 말이 떠올랐다.

"신뢰하되, 검증하라"

벤치마킹

참고로 테스트한 DB는 Aurora MySQL 이다.

spec: db.t3.small
엔진 버전: 5.7.mysql_aurora.2.07.1
JDBC Driver: org.mariadb.jdbc:mariadb-java-client:2.5.4
jOOQ: org.jooq:jooq:3.9.6

테스트 케이스는 총 3가지로 했다.

case 1) 단건 삭제 반복
case 2) batch로 삭제 rewriteBatchedStatements default (false)
case 3) batch로삭제 rewriteBatchedStatements true

결론적으로 rewriteBatchedStatements 스위치가 꺼진 상태에서는 단건 삭제나 batch 삭제나 큰 차이가 없었다.

그런데 rewriteBatchedStatements를 true로 바꾸고 테스트를 해보니 정말 큰 차이가 있었다. (단위 ms)

삭제 크기 case 1 case 2 case 3
1 5.6 6.6 6.2
10 5.3 4.16 0.62
100 4.7 4.524 0.164
1000 4.426 4.3356 0.1166

참고로 위의 표는 삭제 단위로 나누어 건 당 시간이다.

총 걸린 시간 기준의 표는 아래와 같다.

삭제 크기 case 1 case 2 case 3
1 5.6 6.6 6.2
10 53 41.6 6.2
100 470 452.4 16.4
1000 4426 4335.6 116.6

차트로 표현하면 case 1과 2는 선형으로 증가하는데, case 3의 경우 선형으로 증가하나 기울기가 완만함을 알 수 있었다.

참고로 useServerPrepStmts 스위치는 1.3.0 부터 1.6.0 이전까지는 기본값이 true 였다. 1.6.0 부터는 기본값이 false로 바뀌었다. ref

만약 rewriteBatchedStatements 를 true로 바뀌면 이 스위치의 값은 false가 된다.