[Kafka] 파티셔닝과 메시지 키: 순서 보장, hot partition, 파티션 수 정하는 기준
![[Kafka] 파티셔닝과 메시지 키: 순서 보장, hot partition, 파티션 수 정하는 기준](https://blog.kakaocdn.net/dna/odKqf/dJMcaci1cDZ/AAAAAAAAAAAAAAAAAAAAAKryfDVtEcl4kkQv2ZSsqa8EXI1rNhwyXuZSlDJX28ZR/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&expires=1782831599&allow_ip=&allow_referer=&signature=FjcLhNaQqilC44t0CrVhNT5AZAg%3D)
[Kafka] 파티셔닝과 메시지 키: 순서 보장, hot partition, 파티션 수 정하는 기준
카프카에서 메시지가 어느 파티션으로 가는지는 메시지 키가 정해요. 그리고 이 파티션 배치가 순서 보장과 병렬 처리량을 동시에 좌우해요. 키를 아무 생각 없이 주거나 안 주면, 순서가 깨지거나 특정 파티션만 과부하가 걸려요.
파티션과 키의 관계에서 출발해 키가 있을 때와 없을 때의 분배, 순서 보장, 커스텀 파티셔너, hot partition 문제, 파티션 수를 정하는 기준, 자주 만나는 문제 순으로 따라가요. 순서 보장의 프로듀서 쪽 이야기는 프로듀서 acks와 순서 보장 글과 이어져요.
01. 파티션은 병렬과 순서의 단위예요
토픽은 여러 파티션으로 나뉘어요. 이 파티션이 두 가지를 결정해요. 하나는 병렬 처리량이에요. 파티션이 여러 개여야 컨슈머가 나눠 맡아 동시에 처리할 수 있어요. 다른 하나는 순서예요. 카프카는 같은 파티션 안에서만 순서를 보장해요.
그래서 파티션은 "어떻게 나눠 빠르게 처리할까"와 "무엇의 순서를 지킬까"를 동시에 정하는 단위예요. 이 둘이 메시지 키 하나로 연결돼요.
파티션과 복제는 다른 거예요
헷갈리기 쉬운 게 파티션과 복제(replication)예요. 둘은 목적이 달라요. 파티션은 나눠서 빠르게 처리하기 위한 분할이고, 복제는 한 파티션을 여러 브로커에 복사해 장애에 대비하는 안전장치예요.
그래서 파티션을 늘리면 병렬도가 올라가고, 복제 팩터를 늘리면 안전성이 올라가요. 한 파티션은 리더 하나와 보조 복제본 여럿으로 이뤄지고, 읽고 쓰는 건 리더가 맡아요. 이 글은 분배·순서를 정하는 파티션에 집중하고, 복제·acks 쪽은 프로듀서 acks 글에서 다뤘어요.
02. 키가 파티션을 정해요
프로듀서가 메시지에 키를 주면, 카프카는 그 키의 해시로 파티션을 정해요. 대략 hash(key) % 파티션수 방식이에요. 그래서 같은 키는 항상 같은 파티션으로 가요.

// 주문 ID를 키로 → 같은 주문 이벤트는 같은 파티션
kafkaTemplate.send("orders", order.getId(), orderEvent);
이게 순서 보장의 핵심이에요. 한 주문의 "생성 → 결제 → 배송" 이벤트를 주문 ID를 키로 보내면, 셋이 같은 파티션에 순서대로 쌓여요. 그러면 컨슈머가 그 순서대로 처리해요.
03. 키가 없으면 어떻게 되나요
키를 주지 않으면(null) 카프카는 파티션을 알아서 나눠요. 예전에는 라운드로빈으로 한 건씩 돌렸는데, 지금은 sticky partitioner를 써요.
sticky partitioner는 한 배치를 한 파티션에 몰아 보내고, 배치가 차면 다음 파티션으로 옮겨요. 배치를 꽉 채워 보내니 처리량이 좋아요. 대신 키가 없으니 순서 보장은 없어요. 순서가 상관없는 데이터(독립적인 로그·이벤트)라면 키 없이 보내 분배 효율을 챙기는 거예요.
예전 라운드로빈 방식은 메시지마다 파티션을 바꿔서 배치가 잘게 쪼개졌어요. 그래서 같은 무키라도 sticky 방식이 배치 효율과 처리량에서 유리해요. 결국 키를 안 줘도 카프카가 알아서 고르게 나눠주니, "순서가 필요 없으면 키를 비워둔다"가 기본 전략이에요.
04. 순서가 필요하면 키, 분배가 필요하면 무키
정리하면 선택이 단순해져요. 같은 묶음의 순서를 지켜야 하면 그 묶음을 식별하는 키를 줘요. 순서가 상관없고 고르게 빨리 처리하고 싶으면 키를 안 줘요.

주의할 함정. 순서를 위해 너무 굵은 키를 쓰면 안 돼요. 모든 메시지에 같은 키를 주면 전부 한 파티션으로 몰려서, 파티션이 10개여도 1개만 일해요. 병렬 처리가 죽는 거죠. 순서를 지켜야 하는 "가장 작은 단위"를 키로 잡는 게 좋아요. 주문 단위 순서면 주문 ID, 사용자 단위면 사용자 ID처럼요.
05. 커스텀 파티셔너
기본 해시 분배가 아니라 직접 규칙을 정하고 싶으면 Partitioner를 구현해요. 특정 키를 특정 파티션에 고정하거나, 비즈니스 규칙으로 나눌 때 써요.
public class RegionPartitioner implements Partitioner {
@Override
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
int n = cluster.partitionCountForTopic(topic);
return Math.floorMod(((Order) value).getRegion().hashCode(), n); // 지역별 분배 (hashCode 음수 대비)
}
// configure, close 생략
}
다만 커스텀 파티셔너는 분배가 한쪽으로 쏠리지 않게 신중히 설계해야 해요. 잘못 짜면 다음에 볼 hot partition 문제가 생겨요.
06. hot partition, 한 파티션만 과부하
키 분배가 고르지 않으면 특정 파티션에 메시지가 몰려요. 이걸 hot partition이라고 해요. 그 파티션을 맡은 컨슈머만 바빠지고 나머지는 놀아요. 파티션을 아무리 늘려도 한 키에 몰리면 소용없어요.
예를 들어 "VIP 고객 한 명"을 키로 잡았는데 그 고객의 트래픽이 전체의 절반이면, 그 파티션 하나가 절반을 떠안아요. 이럴 땐 키를 더 잘게 쪼개거나(주문 ID 등), 키에 샤딩 값을 덧붙여 분산시켜요. 순서 요구와 분산 사이에서 키 설계를 조정하는 거예요. lag을 파티션별로 봤을 때 한 파티션만 빨갛게 밀려 있다면 이걸 의심해요.
07. 키 설계, 순서 단위를 정확히 잡기
좋은 키는 "순서를 지켜야 하는 가장 작은 단위"예요. 너무 굵으면 쏠리고, 너무 잘면 순서가 안 지켜져요. 도메인별로 보면 이래요.
- 주문 처리 — 주문 ID. 한 주문의 생성·결제·배송이 순서대로 가면 충분하고, 주문끼리는 병렬이어도 돼요.
- 사용자 활동 — 사용자 ID. 한 사용자의 행동 순서가 중요할 때. 단 헤비 유저가 있으면 hot partition을 조심해요.
- 독립 이벤트 — 키 없음. 알림·로그처럼 서로 순서가 상관없으면 키를 안 줘서 고르게 분산해요.
핵심 질문은 "무엇과 무엇 사이의 순서가 꼭 필요한가"예요. 그 답이 키예요. 전체 순서가 필요하다고 착각해 모든 메시지를 한 키로 묶으면 병렬이 죽고, 순서가 필요한데 키를 안 주면 뒤섞여요. 이 경계를 정확히 잡는 게 키 설계의 전부예요.
08. 파티션 수는 어떻게 정하나요
파티션 수는 병렬도의 상한이에요. @KafkaListener 설정 글에서 봤듯, 동시 처리 컨슈머 수는 파티션 수를 못 넘어요. 그래서 미래의 컨슈머 확장까지 보고 넉넉히 잡아요.
파티션 수와 컨슈머 스레드의 1:1 관계를 직접 실험한 기록이 Concurrency 설정 기준(2021)에 있어요.
하지만 무작정 많이 잡으면 안 돼요. 파티션이 많을수록 브로커의 파일 핸들·메모리·리밸런싱 비용이 늘어요. 그리고 중요한 제약이 있어요.

파티션 증설의 두 가지 함정. 첫째, 파티션은 한 번 늘리면 줄일 수 없어요. 둘째, 늘리는 순간 hash(key) % 파티션수의 분모가 바뀌어 같은 키가 전과 다른 파티션으로 가요. 증설 전 이벤트는 P2에, 증설 후 이벤트는 P4에 쌓이는 식으로 같은 주문의 순서가 갈라지는 거죠. 순서가 중요한 토픽은 파티션 수를 처음에 신중히 정하고 가급적 안 바꿔요. 늘려야 한다면 증설 시점의 순서 영향 범위를 미리 따져야 해요.
대략적인 산정은 처리량 기준으로 잡아요. 목표 처리량을 파티션 하나가 감당하는 처리량으로 나누는 거예요. 예를 들어 목표가 초당 10,000건이고 컨슈머 하나(파티션 하나)가 초당 1,000건을 처리하면, 최소 10개가 필요해요. 여기에 트래픽 증가와 컨슈머 확장 여지를 더해 약간 넉넉히 잡아요. 줄일 수 없다는 제약 때문에 "지금 필요한 수"보다는 "가까운 미래까지의 수"로 정하는 게 안전해요.
09. 자주 만나는 문제
이벤트 순서가 가끔 뒤섞여요
키가 없거나, 순서를 지켜야 할 묶음이 서로 다른 파티션에 흩어진 거예요. 같은 묶음을 식별하는 키를 주는지 확인해요.
컨슈머 하나만 계속 바빠요
hot partition이에요. 키가 한쪽으로 쏠리는 거니, 키를 더 잘게 쪼개거나 분산 키로 바꿔요.
파티션을 늘렸더니 순서가 깨졌어요
파티션 수가 바뀌며 키-파티션 매핑이 달라진 거예요. 순서가 중요한 토픽은 파티션 수를 함부로 바꾸지 말고, 늘려야 하면 영향 범위를 미리 따져요.
정리
파티셔닝은 순서와 분배를 동시에 정하는 일이에요. 순서를 지켜야 하는 가장 작은 단위를 키로 잡으면 같은 파티션에 순서대로 쌓이고, 순서가 상관없으면 키 없이 보내 고르게 분산해요. 키가 쏠리면 hot partition이 생기니 분산을 챙기고, 파티션 수는 못 줄이고 바꾸면 순서에 영향을 주니 처음에 신중히 정해요.
키 하나로 묶이는 이 그림은 프로듀서 쪽 acks와 순서 보장, 컨슈머 쪽 @KafkaListener 설정·리밸런싱까지 이어 보면 한층 또렷해져요.
출처: Kafka — Topics and Partitions · Spring for Apache Kafka — Sending Messages
'백엔드' 카테고리의 다른 글
📚 같이 보면 좋은
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 일정액의 수수료를 제공받습니다."