이 글에서 얻는 것
- Kafka를 “메시지 큐”가 아니라 분산 로그(distributed log) 로 이해하고, 토픽/파티션/오프셋의 의미를 설명할 수 있습니다.
- Consumer Group이 어떻게 스케일 아웃을 만드는지(파티션-컨슈머 매핑, 리밸런스)를 이해합니다.
- “정렬(ordered) vs 처리량(throughput)” 트레이드오프를 키 설계/파티션 설계로 풀 수 있습니다.
- 커밋 전략(자동/수동)이 중복/유실에 어떤 영향을 주는지 감각을 잡습니다.
0) Kafka는 한 문장으로: Append-only 로그
Kafka에서 메시지는 “큐에서 꺼내서 사라지는 것”이 아니라, 토픽의 파티션에 순서대로 append 되고, 컨슈머는 오프셋(offset)을 이동하며 읽습니다.
핵심 결과:
- 읽는 쪽(컨슈머)이 많아도 브로커는 로그를 제공하면 됩니다(구독자 확장에 유리).
- “어디까지 읽었는지”는 메시지가 아니라 오프셋 커밋으로 표현됩니다.
1) 기본 용어: Topic / Partition / Offset
- Topic: 메시지 스트림의 이름(예:
order-events) - Partition: 토픽을 여러 로그로 나눈 단위(병렬 처리/확장 단위)
- Offset: 파티션 내부에서 메시지의 위치(0, 1, 2, …)
정렬(ordering)은 “토픽 전체”가 아니라 파티션 내부에서만 보장됩니다.
2) 메시지 흐름: Producer → Topic(Partition) → Consumer Group
- Producer는 레코드를 특정 토픽/파티션에 보냅니다.
- 브로커는 레코드를 디스크 로그에 저장합니다(복제 포함).
- Consumer Group은 파티션을 컨슈머들에게 나눠 줍니다.
중요한 제약:
- 한 파티션은 한 Consumer Group 안에서 동시에 한 컨슈머만 소비합니다.
- 그래서 “컨슈머 수를 늘려도” 파티션 수가 부족하면 더 이상 스케일 아웃이 안 됩니다.
3) Consumer Group과 리밸런스
Consumer Group은 “같은 토픽을 협업해서 처리”하는 단위입니다.
- 컨슈머가 추가/종료되거나,
- 파티션 수가 바뀌면,
파티션 할당을 다시 하는데, 이것이 리밸런스(rebalance) 입니다.
리밸런스가 잦으면:
- 처리 중단/재시도/중복이 늘고,
- 지연이 튈 수 있습니다.
그래서 파티션/컨슈머 증설은 “그냥 늘리기”보다 운영 관측을 기반으로 계획하는 편이 좋습니다.
4) 오프셋 커밋: 중복/유실을 결정한다
Kafka 컨슈머는 “어디까지 처리했다”를 커밋합니다(기본 저장소는 __consumer_offsets).
커밋 위치가 중요한 이유:
- 처리 전에 커밋하면: 실패 시 메시지가 유실될 수 있음(at-most-once 성향)
- 처리 후에 커밋하면: 실패/재시작 시 중복 처리가 생길 수 있음(at-least-once 성향)
현업의 기본은 대체로:
- 처리 후 커밋 + 멱등 처리(중복을 견딘다)
입니다. “정확히 한 번(exactly-once)”은 조건이 많고 운영 난도가 높습니다(뒤 글에서 다룸).
5) 파티션 설계: 키가 곧 정렬/부하 분산 전략이다
5-1) 메시지 키(key)와 정렬
키를 주면 Kafka는 보통 키 해시로 파티션을 선택합니다. 그래서 “같은 키”는 같은 파티션에 들어가고, 그 안에서 순서가 유지됩니다.
예:
- 주문 이벤트를
orderId로 키잉 → 같은 주문의 상태 변화(생성→결제→배송)가 같은 파티션에서 순서 유지
5-2) 핫 파티션(hot partition)
키 분포가 편향되면 특정 파티션만 과부하가 됩니다.
- 파티션이 늘어도, “핫 키” 하나가 병목이면 해결이 안 됩니다.
- 키를 더 잘게 쪼개거나(예:
userId대신userId % N같은 샤딩 키), - 파티션 전략을 다시 설계해야 합니다(정렬 요구와 충돌할 수 있음).
5-3) 파티션 수/복제 팩터
- 파티션 수: 병렬 처리/확장성의 단위(너무 많으면 메타데이터/리밸런스 비용 증가)
- 복제 팩터: 내구성/가용성(보통 3이 많이 쓰임, 환경에 따라)
6) 보관 정책(retention): delete vs compact
Kafka 토픽은 기본적으로 “시간/크기 기반 보관(delete)”을 합니다.
추가로 log compaction은 “키 기준으로 최신 값만 남기는” 형태입니다.
- 이벤트 스트림(히스토리)이 필요하면 delete 기반이 자연스럽고,
- “현재 상태의 스냅샷”이 목적이라면 compact가 도움이 될 수 있습니다.
7) 자주 하는 실수
- “정렬은 토픽 전체에서 된다”고 착각(파티션 내부만)
- 파티션을 너무 많이 만들어 운영/리밸런스 비용이 커짐
- 키를 안 넣어(null key) 정렬/부하 분산이 통제되지 않음
- 커밋 전략이 불명확해서 중복/유실이 랜덤으로 발생
연습(추천)
order-events토픽을 만든다고 가정하고, 메시지 키를 무엇으로 잡을지(정렬 요구 포함) 설계해보기- “컨슈머 4개로 처리량을 늘리고 싶다”면 파티션 수/키 분포를 어떻게 설계해야 하는지 적어보기
- 처리 후 커밋 전략에서 중복이 생길 수 있는 케이스를 2~3개 적고, 멱등 처리 방식을 정리해보기
💬 댓글