티스토리 뷰
Spring Batch를 사용하다 보면 가장 자주 마주치는 설정 중 하나가 chunk size입니다. 처음에는 단순히 “한 번에 몇 건씩 처리할 것인가” 정도로 이해하기 쉽습니다. 하지만 공식 문서를 다시 읽어보면, chunk는 단순한 반복 단위가 아니라 트랜잭션 경계라는 점을 분명히 알 수 있습니다.
Spring Batch Reference Documentation에서 설명하듯이, chunk 기반 Step은 지정된 개수만큼 read–process–write를 수행한 뒤 하나의 트랜잭션으로 commit됩니다. 즉 chunk size는 곧 트랜잭션 크기입니다. 이 지점을 어떻게 이해하느냐에 따라 시스템의 안정성과 성능이 달라집니다.
이 글에서는 chunk size를 단순한 성능 튜닝 수치가 아니라, 비용 모델 관점에서 어떻게 바라보아야 하는지 정리해 보려고 합니다. 특히 다음 두 가지 상황을 중심으로 살펴보겠습니다.
- DB에 직접 벌크 쓰기를 수행하는 경우
- DB 쓰기를 하지 않고 내부 API 호출로 대체하는 경우
chunk 기반 처리의 본질은 트랜잭션 경계입니다
Spring Framework의 트랜잭션 문서에서도 확인할 수 있듯이, 트랜잭션은 논리적 작업 단위를 정의합니다. Spring Batch에서는 이 논리적 작업 단위를 chunk size로 제어합니다.
예를 들어 chunk size가 100이라면, 100건의 아이템이 write 단계까지 정상 처리된 이후 commit이 발생합니다. 그 사이에 예외가 발생하면 해당 chunk는 rollback됩니다. 따라서 chunk size는 다음을 동시에 결정합니다.
- 한 번에 유지되는 DB lock 범위
- WAL/redo 로그에 기록되는 변경량
- rollback 시 복구해야 할 데이터 범위
- commit 호출 횟수
결국 chunk size는 “한 트랜잭션이 감당해야 하는 비용”을 정의하는 설정이라고 이해하는 편이 더 정확하다고 생각합니다.
DB에 직접 벌크 쓰기를 수행하는 경우
DB에 직접 쓰기를 수행하는 경우, 병목은 대체로 디스크 IO입니다. 여기서는 네트워크가 아니라 스토리지와 로그 시스템의 특성이 더 큰 영향을 줍니다.
트랜잭션 크기와 WAL/Redo 로그
MySQL InnoDB, PostgreSQL, Oracle과 같은 주요 RDBMS는 모두 Write-Ahead Logging 구조를 사용합니다. 데이터 파일이 변경되기 전에 redo 또는 WAL 로그가 기록되고, commit 시점에 flush가 일어납니다.
chunk size가 커질수록 한 트랜잭션에서 생성되는 WAL/redo 로그 양이 증가합니다. 이 양이 커질수록 다음과 같은 현상이 발생할 수 있습니다.
- 로그 버퍼 사용량 증가
- 디스크 flush 지연
- fsync 비용 증가
- 복제 지연(replication lag) 확대
Designing Data-Intensive Applications에서도 설명하듯이, 로그 기반 시스템에서 commit은 단순한 메서드 호출이 아니라 디스크 동기화와 밀접하게 연결되어 있습니다. 큰 트랜잭션은 시스템 전체 지연을 유발할 가능성이 있습니다.
undo 세그먼트와 MVCC 비용
MVCC를 사용하는 DB에서는 트랜잭션이 오래 지속될수록 이전 버전의 레코드를 더 오래 유지해야 합니다. chunk size가 커지면 트랜잭션 지속 시간이 길어지고, undo 로그와 버전 관리 비용이 증가합니다.
이로 인해 다음과 같은 문제가 발생할 수 있습니다.
- vacuum 또는 purge 지연
- 버전 체인 증가
- 읽기 쿼리 성능 저하
chunk size는 단순히 “몇 건씩 쓰는가”의 문제가 아니라, “얼마나 오래 트랜잭션을 유지하는가”의 문제와 연결됩니다.
lock 유지 시간과 경합
InnoDB와 같은 엔진에서는 트랜잭션이 commit될 때까지 row lock이 유지됩니다. chunk size가 크면 lock이 유지되는 시간이 길어집니다. 이는 동시성 환경에서 lock wait을 유발할 수 있습니다.
특히 운영 DB와 배치 DB가 분리되지 않은 환경에서는, chunk size 설정이 실시간 트랜잭션에 영향을 줄 수 있습니다.
chunk size 선택을 위해 검토해야할 지표
DB 직접 쓰기 환경에서는 다음 관점에서 판단하는 것이 합리적이라고 생각합니다.
- 트랜잭션 지속 시간이 과도하게 길어지지 않는지
- WAL 생성량이 급격히 증가하지 않는지
- replication delay가 허용 범위를 넘지 않는지
- lock wait이 발생하지 않는지
결국 chunk size는 “트랜잭션당 디스크 IO 비용”이 감당 가능한 수준에서 결정되어야 합니다. commit 횟수를 줄이는 것만이 목표가 되어서는 안 된다고 생각합니다.
DB 쓰기를 내부 API 호출로 대체하는 경우
내부 API 호출로 쓰기 작업을 대체하는 경우, 비용 모델은 완전히 달라집니다. 여기서는 디스크 IO보다 네트워크와 원격 시스템 처리량이 더 중요한 요소가 됩니다.
네트워크 RTT와 직렬화 비용
API 호출은 네트워크 round-trip time의 영향을 받습니다. 또한 요청과 응답은 직렬화/역직렬화 과정을 거칩니다. chunk size가 커질수록 한 번의 트랜잭션 내에서 수행되는 API 호출 횟수가 증가합니다.
이때 고려해야 할 점은 다음과 같습니다.
- 외부 시스템의 처리 속도
- 요청 큐 적체 가능성
- thread pool 고갈 가능성
분산 시스템에서의 트랜잭션 경계
Spring Batch의 로컬 트랜잭션은 API 호출 성공 여부만 관리합니다. 외부 시스템 상태까지 ACID하게 묶을 수는 없습니다. 즉, 로컬 commit이 외부 시스템의 상태와 항상 일치한다고 보장할 수는 없습니다.
이 경우 chunk size가 커질수록 다음과 같은 부담이 증가합니다.
- 부분 실패 발생 시 재처리 범위 증가
- 멱등성 설계 복잡도 증가
- 재시도 비용 증가
Designing Data-Intensive Applications에서도 분산 시스템에서는 네트워크 오류와 부분 실패를 전제로 설계해야 한다고 설명합니다. 이런 관점에서 보면, chunk size는 실패 복구 전략과도 연결되어 있습니다.
backpressure와 외부 시스템 보호
외부 API 서버는 자체적인 처리 한계를 가집니다. chunk size가 커지면 단시간에 더 많은 요청이 집중될 수 있습니다. 이는 backpressure 상황을 유발할 수 있습니다.
특히 외부 시스템이 rate limit을 적용하고 있다면, chunk size는 단순한 성능 설정이 아니라 운영 안정성과 직접 연결됩니다.
내부 API 호출 환경에서의 chunk size 판단 기준
이 경우에는 다음과 같은 지표를 관찰하는 것이 합리적입니다.
- API 평균 응답 시간
- 타임아웃 발생률
- 재시도 발생 비율
- 외부 시스템 CPU 및 큐 길이
여기서는 “트랜잭션당 디스크 IO 비용”이 아니라 “트랜잭션당 네트워크 및 원격 처리 비용”이 기준이 됩니다.
두 방식의 근본적인 차이
DB 직접 쓰기는 로컬 ACID 트랜잭션 안에서의 IO bound 문제입니다. 로그 기록, 디스크 flush, lock 유지 시간이 핵심 변수입니다.
반면 내부 API 호출 방식은 네트워크 bound 문제이며, 분산 시스템의 정합성 모델을 고려해야 합니다. 여기서는 Strong Consistency보다 Eventual Consistency 맥락에서 사고하는 것이 자연스럽습니다.
DB 직접 쓰기에서는 트랜잭션 원자성이 비교적 명확하게 정의됩니다. 그러나 API 호출 기반 구조에서는 트랜잭션 경계가 시스템 경계를 넘지 못합니다. 이 차이가 chunk size 결정 방식의 차이를 만듭니다.
정리하며
Spring Batch의 chunk size는 단순한 숫자 튜닝 파라미터가 아닙니다. 그것은 트랜잭션 경계이며, 한 번의 commit이 감당해야 하는 비용을 정의하는 설정입니다.
DB 직접 쓰기 환경에서는 디스크 IO와 로그 시스템을 이해해야 하고, 내부 API 호출 환경에서는 네트워크와 분산 시스템 특성을 이해해야 합니다.
저 역시 처음에는 chunk size를 “몇으로 하면 빠를까”라는 질문으로 접근했던 적이 있습니다. 하지만 공식 문서와 데이터 중심 애플리케이션 설계 서적을 다시 읽으면서, 결국 중요한 것은 숫자가 아니라 비용 모델이라는 점을 조금씩 이해하게 되었습니다.
앞으로도 배치 시스템을 설계할 때는, chunk size를 성능 튜닝 옵션이 아니라 트랜잭션 단위의 비용을 정의하는 설계 요소로 바라보려고 합니다. 이런 관점이 쌓여야 시스템을 더 안정적으로 운영할 수 있다고 생각합니다.
'STUDY' 카테고리의 다른 글
- Total
- Today
- Yesterday
- DB 트랜잭션
- Cache Avalanche
- Cache Aside
- InterruptedException
- Hot Key 문제
- Redis 성능 개선
- Java Performance
- mybatis
- Spring Batch
- Redis 캐시 전략
- 백엔드 아키텍처
- 캐시 성능 비교
- DB 인덱스 성능
- 트래픽 처리
- Eager Initialization
- 백엔드 성능
- Enum 기반 싱글톤
- TTL 설계
- 백엔드 성능 튜닝
- 동시성처리
- 캐시와 인덱스
- spring batch 5
- 스레드 생명주기
- Initialization-on-Demand Holder Idiom
- Redis vs DB
- Double-Checked Locking
- 트랜잭션 관리
- 캐시 장애
- 백엔드 성능 설계
- Cache Penetration
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |

