OOM의 늪에서 탈출하여 대규모 데이터를 맛있게 쪼개 먹는 파티셔닝과 커서 기반 최적화 전략
카카오페이 정산플랫폼팀의 실제 장애 해결 사례를 통해 대규모 배치 처리의 정석을 보여주는 아티클입니다. 단순한 병렬 처리를 넘어 MongoDB의 내부 동작 원리를 고려한 Reader와 Writer 최적화 기법을 심도 있게 다룹니다. 성능 측정 지표를 통해 실제 아키텍처 개선이 가져오는 압도적인 효율성을 증명합니다.
Spring Batch를 사용하면서 대량 데이터 처리 시 OOM을 겪고 있거나 배치 수행 시간 단축이 절실한 개발자에게 강력히 추천합니다. 특히 MongoDB를 저장소로 사용하는 환경에서 Cursor와 Bulk 연산을 어떻게 조합해야 하는지 명확한 가이드를 제공합니다.
수억 건에 달하는 대용량 원장 데이터를 Spring Batch로 처리하는 과정에서 데이터를 한꺼번에 메모리에 적재하며 OOM(Out of Memory) 이슈가 발생했습니다. 또한, 기존의 페이징 기반 ItemReader는 데이터가 뒤로 갈수록 skip() 연산에 따른 성능 저하가 심화되는 병목 현상을 보였습니다.
전체 작업을 월/일 단위로 나누는 2단계 파티셔닝(Local Partitioning) 전략을 도입하여 병렬성을 확보했습니다. 메모리 효율을 위해 Paging 대신 Cursor 기반의 MongoCursorItemReader로 스트리밍 조회를 적용하고, Bulk Operations를 통해 쓰기 성능을 최적화했습니다.
500만 건의 데이터 처리 기준으로 기본 방식 대비 약 10.9배의 속도 향상(97분에서 9분으로 단축)을 달성했습니다. 커서 기반 조회를 통해 메모리 사용량을 일정하게 유지함으로써 수억 건의 데이터 환경에서도 OOM 없이 안정적인 시스템을 구축했습니다.
Trade-off
파티셔닝 도입으로 인해 gridSize 및 ThreadPool 설정 등 구성 복잡도가 증가하며, 병렬 스레드 수 증가에 따른 DB 커넥션 풀 고갈 위험이 존재합니다. 본문에 구체적으로 명시되지는 않았으나, 파티셔닝 작업 간의 데이터 정합성 보장을 위한 추가적인 관리 비용이 발생할 수 있습니다.
단일 JVM 내에서 대량 데이터를 논리적인 조각으로 나누고, 각 파티션을 별도의 스레드에서 독립적인 Worker Step으로 실행하는 병렬 처리 모델입니다.
MongoDB의 서버 사이드 커서를 사용하여 데이터를 스트리밍 방식으로 한 건씩 읽어오는 ItemReader로, 페이징 방식의 한계를 극복합니다.
여러 개의 쓰기 요청을 하나의 메시지로 패키징하여 한 번의 네트워크 왕복으로 처리하는 일괄 쓰기 방식입니다.