LLM WikiAccess-protected knowledge portal
← 스터디 홈
3편 · 약 14분

파티셔닝과 오프셋 관리

파티션은 왜 존재하는가

Kafka 토픽 하나가 단일 브로커의 단일 파일이라면, 쓰기 처리량은 디스크 하나의 한계에 묶인다. 파티션은 토픽을 수평으로 쪼개 여러 브로커에 분산시키는 단위다. 파티션 수 × 브로커 수만큼 병렬 쓰기와 병렬 읽기가 가능해진다.

파티션 내부에서 레코드는 오프셋(offset) 이라는 단조 증가 정수로 식별된다. 오프셋은 파티션 범위 안에서만 유일하다. 토픽 전체에서 유일한 식별자는 (파티션 번호, 오프셋) 쌍이다.

Topic: orders
Partition 0 0 1 2 3 ←
Partition 1 0 1 2 ←
Partition 2 0 1 2 3 4 ←
파티션 내 오프셋은
독립적으로 증가
← 는 현재 LEO
(Log End Offset)
토픽·파티션·오프셋의 구조

파티셔닝 전략

메시지가 어느 파티션에 기록될지는 프로듀서의 파티셔너가 결정한다.

키 기반 파티셔닝

ProducerRecord에 키가 있으면 murmur2(key) % numPartitions로 파티션을 고정한다. 같은 키는 항상 같은 파티션으로 가므로, 파티션 내 순서가 보장된다. 주문 ID를 키로 쓰면 동일 주문의 이벤트(생성 → 결제 → 배송)가 순서대로 도착한다.

주의: 파티션 수를 늘리면 동일 키가 다른 파티션으로 라우팅될 수 있다. 순서가 중요한 토픽은 파티션 수를 초기에 넉넉하게 설계한다.

키 없는 라운드로빈 vs Sticky Partitioner

키가 null이면 기본 동작이 버전별로 다르다.

버전기본 파티셔너동작
Kafka < 2.4DefaultPartitioner (round-robin)레코드마다 파티션을 순환
Kafka ≥ 2.4StickyPartitioner배치가 채워지거나 linger.ms가 지날 때까지 같은 파티션에 누적

Sticky Partitioner는 브로커 한 곳에 배치를 모아 쓰므로 압축·쓰기 효율이 높다.

커스텀 파티셔너

org.apache.kafka.clients.producer.Partitioner 인터페이스를 구현하면 비즈니스 규칙으로 파티션을 지정할 수 있다. 예를 들어 VIP 사용자의 이벤트를 별도 파티션에 라우팅해 전용 컨슈머가 처리하게 할 수 있다.

파티션 수 결정 기준

파티션이 많을수록 처리량이 늘지만 메타데이터 오버헤드와 리밸런싱 비용도 커진다.

실무에서 쓰는 근거:

  • 목표 처리량 / 파티션당 처리량 으로 시작. 단일 파티션은 보통 수십 MB/s를 처리할 수 있다.
  • 컨슈머 인스턴스 수보다 파티션 수가 많아야 병렬 소비가 가능하다. 컨슈머 수 = 파티션 수가 최대 병렬도다.
  • 추후 스케일아웃을 고려해 처음부터 여유 있게 잡는다(키 순서 보장 토픽은 변경이 어렵다).
  • Kafka 공식 문서는 브로커당 파티션 수 4000개, 클러스터 전체 200,000개를 권고 상한으로 제시한다 (KRaft 기준).

오프셋의 세 가지 위치

컨슈머 입장에서 파티션에는 세 가지 의미 있는 오프셋 위치가 있다.

용어설명
LEO (Log End Offset)리더가 다음에 쓸 오프셋. 현재 마지막 메시지 + 1.
HW (High Watermark)모든 ISR에 복제 완료된 마지막 오프셋. 컨슈머는 HW 이하만 읽을 수 있다.
커밋 오프셋 (Committed Offset)컨슈머 그룹이 __consumer_offsets에 기록한 처리 완료 지점.

LEO와 HW의 차이 구간(HW ~ LEO)은 복제 중인 미확정 메시지다. 리더 장애 시 이 구간이 롤백될 수 있으므로 컨슈머에게는 노출하지 않는다.

__consumer_offsets: 오프셋 저장소

컨슈머 그룹이 commitSync() / commitAsync()를 호출하면 오프셋은 브로커 내부의 특수 토픽 __consumer_offsets에 기록된다. 기본 파티션 수는 50개이며, 그룹 ID를 해시해 어느 파티션(=어느 브로커)이 그룹 코디네이터 역할을 맡을지 결정한다.

레코드 키는 (group.id, topic, partition) 튜플이다. 이 토픽은 로그 컴팩션 으로 관리되므로, 같은 키의 오래된 레코드는 주기적으로 정리되고 최신 오프셋만 남는다. 덕분에 컨슈머를 재시작해도 커밋된 오프셋을 빠르게 복원할 수 있다.

Consumer Group 컨슈머 A 컨슈머 B
commitSync()
__consumer_offsets (내부 토픽)
key: (group, topic, partition) value: offset + metadata
로그 컴팩션 → 최신값 유지
재시작 / 리밸런싱 후 마지막 커밋 오프셋 조회
이어서 소비 재개
__consumer_offsets 동작 구조

auto.offset.reset: 커밋이 없을 때

처음 소비하거나 커밋 오프셋이 만료·삭제된 경우 auto.offset.reset 설정이 적용된다.

  • latest (기본값): 구독 시점 이후의 신규 메시지만 소비. 과거 데이터를 놓친다.
  • earliest: 파티션의 가장 오래된 메시지부터 소비. 재처리나 신규 그룹에 유용.
  • none: 커밋 오프셋이 없으면 예외 발생. 운영 환경에서 실수 방지용.

파티션 수 변경 시 주의사항

파티션을 늘리는 것은 가능하지만 줄이는 것은 지원되지 않는다. 키 기반 파티셔닝을 쓰는 토픽에서 파티션을 늘리면 hash(key) % numPartitions 결과가 바뀌어 기존 키의 메시지가 다른 파티션으로 갈 수 있다. 이를 허용하지 않는다면 마이그레이션 전략(새 토픽으로 복사, 커넥터 재설정 등)이 필요하다.

Open Questions

  • KIP-848(Next-Gen Consumer Rebalance Protocol)은 Kafka 4.0에서 조기 접근 기능으로 포함됐으나, 일반 GA 여부는 배포판별로 확인 필요.

References

  • https://kafka.apache.org/documentation/#design_offsetmanagement
  • https://www.conduktor.io/glossary/kafka-topics-partitions-brokers-core-architecture
  • https://oneuptime.com/blog/post/2026-01-27-kafka-topics-partitions-design/view
  • https://oneuptime.com/blog/post/2026-01-30-kafka-consumer-assignment-strategies/view
  • https://oneuptime.com/blog/post/2026-01-25-manage-consumer-offsets-kafka/view
  • https://www.automq.com/blog/kafka-offsets-best-practices
  • https://www.confluent.io/blog/guide-to-consumer-offsets/
  • https://cwiki.apache.org/confluence/display/KAFKA/Offset+Management