백엔드 데이터 인프라 86편. Kafka Producer 설계 — partition 선택 (round-robin·sticky·key hash), ACK 3가지 정책 (acks=0/1/all), idempotent producer 의 exactly-once 보장, retries·linger 같은 핵심 파라미터까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 86편이에요. 85편 까지 디스크·zero-copy·batching·compression 의 효율 비밀을 잡았다면, 이번 86편은 Producer 측 핵심 설계 — partition 선택·ACK·idempotent 를 다뤄요.
Kafka Producer 설계가 어렵게 느껴지는 이유
Producer 는 "메시지 보내기 끝" 처럼 간단해 보이지만, 실제로는 4가지 결정이 숨어 있어요.
첫째, partition 선택. partition 은 topic 안에서 메시지를 나눠 담는 칸인데, 같은 topic 에 N개가 있는 상황에서 어디로 보낼지가 순서 보장과 부하 분산을 좌우해요.
둘째, ACK 정책 trade-off. ACK 는 broker 가 메시지를 받았다고 알려주는 응답이고, acks=0/1/all 이 각각 정확히 무슨 뜻인지·어떤 데이터 손실 위험이 있는지 알아야 해요. 잘못 설정하면 메시지가 조용히 사라져요.
셋째, Idempotent Producer 와 exactly-once. idempotent 는 같은 요청을 여러 번 보내도 결과가 한 번과 같은 성질을 말해요. retries 가 발생할 때 중복 메시지 위험을 어떻게 막는지가 핵심.
이 글에서 4가지 Producer 설계 결정을 깊이 정리할게요.
Partition 선택 — 3가지 알고리즘
Producer 가 같은 topic 의 N개 partition 중 어디로 보낼지 정하는 방식이 3가지 있어요.
(1) Key 기반 — 같은 key = 같은 partition
producer.send(new ProducerRecord<>("orders", "user42", "buy item"));
key 가 있으면:
partition = hash(key) % num_partitions
같은 user42 의 모든 이벤트는 항상 같은 partition 으로 가요. 순서 보장의 핵심.
(2) Round-Robin (Key 없음)
key 가 null 이면 partition 들에 균등 분산돼요. 부하 분산은 좋지만 순서 보장은 안 돼요.
(3) Sticky Partitioner — Kafka 2.4+ 기본
순수 round-robin 의 문제는 batch 효율이 떨어진다는 거예요. 한 batch 안 메시지가 여러 partition 으로 흩어지면 각 partition 의 batch 가 작아지거든요.
Sticky Partitioner 는 메시지를 보내는 partitioner(분배기) 중 하나로, 한 batch 가 채워질 때까지 한 partition 에 몰아 넣어요. batch 가 다 차면 다음 partition 으로 넘어가요. batching 효율과 균등 분산을 둘 다 잡는 방식.
시간 0~10ms: partition 0 에 batch 가득
시간 10~20ms: partition 1 에 batch 가득
시간 20~30ms: partition 2 에 batch 가득
...
key 없는 환경에서 처리량이 30% 이상 올라가요.
Custom Partitioner
public class MyPartitioner implements Partitioner {
@Override
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
// 비즈니스 로직 기반 partition 선택
return ...;
}
}
특수 환경 (예: VIP 사용자는 별도 partition) 에 써요.
ACK 정책 — 3가지 trade-off
가장 중요한 trade-off 가 여기에 있어요. 데이터 안전성과 지연 시간이 정면으로 부딪치는 자리.
acks=0 — Fire-and-Forget
Producer → Broker (응답 안 기다림) → 다음 메시지
- 장점 — 최고 처리량, 최저 지연
- 단점 — broker 죽으면 메시지 손실, 메시지 도착 보장 X
- 사용 — 로그·메트릭처럼 손실이 OK 인 데이터
acks=1 (기본) — Leader 확인
Producer → Leader 가 자신 디스크에 write → ACK → 다음 메시지
- 장점 — 빠르고 일반적으로 안전
- 단점 — Leader 가 ACK 후 죽고 replica 로 복제가 안 됐으면 메시지 손실
- 사용 — 대부분의 일반 데이터
acks=all (또는 -1) — 모든 ISR 확인
Producer → Leader → 모든 ISR(in-sync replicas) 가 받음 → ACK → 다음
- 장점 — 가장 안전, replica 까지 보장
- 단점 — 지연 시간 ↑, 처리량 ↓
- 사용 — 금융·결제·법적 자료처럼 손실이 X 여야 하는 데이터
min.insync.replicas 와의 조합
여기서 정말 중요한 자리예요. acks=all 만으로는 부족하고, broker 설정 min.insync.replicas 와 함께 써야 해요.
replication.factor=3
min.insync.replicas=2
acks=all
최소 2개 ISR 가 확인해야 ACK 가 떨어진다는 뜻이에요. 1개만 살아 있으면 producer 가 NotEnoughReplicasException 을 던지면서 쓰기에 실패해요.
trade-off:
min.insync.replicas=1+acks=all= leader 만 확인 (사실상 acks=1)min.insync.replicas=2+replication.factor=3= 권장 안전 설정min.insync.replicas=3+replication.factor=3= broker 한 대만 down 돼도 쓰기 실패
Idempotent Producer — 중복 방지
문제
Producer 가 메시지를 보내요. broker 가 받고 ACK 를 보냈는데, 네트워크 에러로 Producer 가 ACK 를 못 받아요. 그러면 Producer 가 retry 를 하고, broker 는 같은 메시지를 두 번 받게 돼요.
기본 동작에서는 중복 메시지가 broker 에 두 번 저장돼요.
해법 — Idempotent Producer
enable.idempotence=true
설정 시:
- Producer 가 unique Producer ID (PID) 받음
- 각 메시지에 sequence number 부여
- Broker 가 (PID, sequence) 추적 → 중복 거부
같은 메시지를 두 번 전송해도 한 번만 저장.
자동으로 활성화되는 설정:
acks=allretries > 0max.in.flight.requests.per.connection ≤ 5
Kafka 3.0+ 부터 enable.idempotence=true 가 기본값.
Exactly-Once 보장
Idempotent + Transactions (다음 글) 를 조합하면 exactly-once semantics (EOS) 가 돼요. 정말 정확히 한 번을 보장하는 자리. 결제·금융 같은 데이터가 여기 들어가요.
Retries — 일시적 실패 자동 복구
retries=2147483647 # 거의 무한 (기본)
retry.backoff.ms=100 # 재시도 사이 100ms
delivery.timeout.ms=120000 # 총 2분 안에 ACK 못 받으면 실패
일시적 네트워크 에러나 broker 일시 다운은 자동으로 retry 가 돼요. idempotent 와 같이 쓰면 중복 없이 동작.
여기서 시험 함정이 하나 있어요 — retry 가 메시지 순서를 깨뜨릴 수 있어요. max.in.flight.requests.per.connection=5 면 5개 in-flight 중 1개가 실패하고 retry 가 도는 사이에 나머지 4개가 먼저 도착해서 순서가 깨져요.
해법:
max.in.flight.requests.per.connection=1(느림) 또는enable.idempotence=true— sequence 로 순서 보장 + retry 안전
Batching + linger.ms — 효율과 지연 균형
85편에서 본 내용. Producer 설계 핵심:
batch.size=16384 # 16KB 또는
linger.ms=10 # 10ms 후 즉시 전송
buffer.memory=33554432 # 32MB 메모리 buffer
batch.size까지 채우거나linger.ms시간 지나면 전송- buffer 가득 차면
send()가 block
비동기 vs 동기 send
비동기 (권장)
producer.send(record, (metadata, exception) -> {
if (exception != null) {
// 실패 처리
} else {
System.out.println("Sent to partition " + metadata.partition());
}
});
장점 — 높은 처리량 + batching 이 자연스럽게 일어남
동기
RecordMetadata metadata = producer.send(record).get();
장점 — 즉시 실패를 잡을 수 있고 단순함 단점 — 매우 느려요. 사실상 batch 효율을 못 살림
대부분의 환경은 비동기를 써요.
Compression 과 합쳐서
acks=all
enable.idempotence=true
compression.type=zstd
linger.ms=10
batch.size=32768
운영 환경 표준이에요. 안전성과 효율을 함께 잡는 조합.
한계·실무 함정
1. acks=0 잘못 사용
"빠르게" 만 보고 acks=0 을 쓰면 프로덕션에서 데이터 손실이 나요. 로그·메트릭 외에는 최소한 acks=1 부터 시작.
2. min.insync.replicas 미설정
acks=all 만 박고 broker 측 min.insync.replicas=1 이면 사실상 acks=1 이에요. 둘 다 같이 설정해야 안전.
3. Idempotent 비활성 + 무한 retries
중복 메시지가 폭증해요. Kafka 3.0+ 기본값 이라 안전하지만, 이전 버전은 명시적으로 설정.
4. linger.ms 너무 큼 = 지연 폭증
실시간 환경에서는 낮은 linger 를 쓰세요 (1~5ms).
5. 큰 메시지 + 작은 max.request.size
max.request.size=1048576 # 기본 1MB
1MB 초과 메시지는 RecordTooLargeException 이 떨어져요. 큰 메시지 환경은 따로 조정.
시험 직전 한 번 더 — Kafka Producer 함정 압축 노트
- Partition 선택 3가지 = key hash · round-robin · Sticky (2.4+ 기본)
- key 있음 =
hash(key) % N→ 같은 key 같은 partition 순서 보장 - key 없음 (round-robin) = 균등 분산, 순서 보장 X
- Sticky Partitioner = batch 다 찰 때까지 한 partition, 처리량 30%+
- Custom Partitioner = 비즈니스 로직 기반
- ACK 3가지 =
0(fire-and-forget) ·1(leader 확인, 기본) ·all(모든 ISR) acks=0= 손실 OK 데이터만acks=1= 일반, leader 죽으면 손실 가능acks=all= 가장 안전, replica 까지acks=all+min.insync.replicas=2+replication.factor=3= 권장 안전 설정min.insync.replicas안 박으면 사실상 acks=1- Idempotent Producer (
enable.idempotence=true) = 중복 메시지 방지 - 메커니즘 = (PID, sequence) → broker 중복 거부
- Kafka 3.0+ 기본값
- 자동 활성 = acks=all, retries > 0, max.in.flight ≤ 5
- Exactly-Once = Idempotent + Transactions (87편)
- Retries = 일시적 실패 자동 복구
- 함정 — retry 가 순서 깨뜨릴 수 있음 → max.in.flight=1 또는 idempotent
- Batching =
batch.size+linger.ms+buffer.memory - 비동기 send = 표준 (
callback패턴) - 동기 send = batch 효율 X, 특수한 경우만
- 운영 표준 = acks=all + idempotent + zstd 압축 + linger 10ms
- 함정 — acks=0 프로덕션 데이터 손실
- 함정 — min.insync.replicas 미설정
- 함정 — linger.ms 너무 큼 → 지연 폭증
- 함정 — max.request.size 초과 → RecordTooLargeException
공식 문서: Kafka Design — The Producer 에서 자세한 사양을 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 81편 — Kafka 활용 영역 7가지 (메시징·활동 추적·로그·이벤트 소싱)
- 82편 — Kafka Quickstart (5분 hands-on · topic·producer·consumer)
- 83편 — Kafka Design: Motivation (왜 이렇게 설계됐나)
- 84편 — Kafka Design: Persistence (디스크가 빠르다)
- 85편 — Kafka Design: Efficiency (Zero-Copy · Batch 압축)
다음 글:
- 87편 — Kafka Design: Consumer (Pull · Consumer Group · Offset)
- 88편 — Kafka Message Delivery Semantics (at-most·at-least·exactly-once)
- 89편 — Kafka Replication (ISR · Leader Election · Unclean)
- 90편 — Kafka API 5종 종합 (Producer · Consumer · Streams · Connect · Admin)
- 91편 — Kafka Producer API 깊이 (Serializer · Callback · Interceptor)