Batch Performance 극한으로 끌어올리기: 1억 건 데이터 처리를 위한 노력 / if(kakao)2022 영상을 보고 정리한 내용입니다.
흠...오후 4시에 상품 주문 배송 정보를 고객들에게 문자로 일괄 전송해야 하는구나.
간단하네! Batch로 개발해서 16시에 스케줄을 걸어놔야지!
기존에 저장된 정보를 조합해서 새로운 정보를 만들 때
이미 저장된 데이터를 일괄로 수정할 때
통계 형식의 데이터를 만들 때
카카오페이는 늘어나는 데이터량에도 불구하고 Batch 수행시간을 5년전과 비슷한 수준으로 유지하고 있음
Batch 성능 개선의 첫 걸음 = Reader 개선
(Batch 성능에서 차지하는 비중이 Reader가 가장 높기 때문)
Reader의 복잡한 조회 조건이 배치 전체의 성능을 크게 좌우
따라서 SELECT 쿼리를 튜닝하는 것만으로도 극적으로 성능이 개선됨
Chunk Processing을 위한 Pagination Reader를 사용할 수 있음
(Limit, Offset 사용)
하지만 Offset이 커질수록 성능이 크게 저하
(MySQL은 N번째 아이템을 찾는 작업에 큰 부담을 느낌)
그래서 Offset을 항상 0으로 유지하고 직전 페이지의 마지막 id보다 큰 조건을 넣는 방식 사용
Offset이 0이기 때문에 조회 속도 저하가 없음
이때, 편하고 안전하게 쿼리를 작성하기 위해 QueryDSL을 사용
MySQL Cursor는 데이터가 없을 때까지 일정 개수릐 데이터를 반복해서 가져오는 방식
(Chunk Processing에 잘 어울리는 방식)
JpaCursorItemReader는 아예 사용을 안하는 것을 추천
JdbcCursorItemReader는 Native Query를 사용해야 함
그래서 안전하고 세련된 쿼리 구현 방식으로 Exposed 사용
통계 -> Batch -> GroupBy & Sum
데이터가 적을 때는 합리적이고 개발도 간단한 흐름
하지만, 데이터가 많아지고 쿼리가 복잡해지면 문제 발생
쿼리 자체가 너무 느려서 개선된 ItemReader가 무용지물이 됨
쿼리는 단순하게!
- 그냥 GroupBy를 안 쓰면 되겠네
- 직접 Aggregation을 하면 되겠네
하지만 직접 Aggregation을 위한 대용량 리소스를 애플리케이션에서 할당하기는 거의 불가능
(이를 위한 새로운 아키텍처 필요)
하지만 네트워크 I/O 레이턴시 문제가 생김
(1000만 개의 데이터를 합상하기 위해 1000만번의 요청 필요)
이를 해결하기 위해 Redis Pipeline을 사용해서 다수의 Command를 묶어서 처리
(Spring Data Redis로는 처리할 수 없는 부분이기 때문에 카카오페이에서는 자체 라이브러리를 개발해서 사용중)
따라서, Writer에서 JPA를 포기하고 Batch Insert할 것