백엔드 데이터 인프라 보강편. 설정 노브는 다 아는데 '어느 목표를 위해 어느 방향으로 돌릴지'가 안 잡힐 때. Kafka 성능 튜닝을 처리량·지연·내구성·가용성 네 목표의 트레이드오프로 묶고, Broker·Producer·Consumer 노브를 목표별로 정리한 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈의 보강편이에요. 96편 Producer 설정·97편 Consumer 설정·94편 Broker 설정에서 노브를 하나하나 다 봤죠. 그런데 막상 튜닝하려고 하면 막혀요. "이 노브를 어느 방향으로 돌려야 하지?" 노브 설명은 흩어져 있고, 정작 필요한 건 "내 목표를 위해 어느 노브를 어느 방향으로" 인데 그게 안 잡히거든요. 이 글은 Kafka 성능 튜닝을 목표별로 다시 묶어요.
성능 튜닝이 어려운 건 노브가 많아서가 아니에요
설정 글을 다 읽고도 튜닝이 막막한 이유는, 노브를 몰라서가 아니에요. 노브끼리 서로 상충하기 때문이에요. 처리량을 끌어올리면 지연이 늘고, 내구성을 높이면 처리량이 깎여요. 하나를 당기면 다른 하나가 밀려나요.
그래서 성능 튜닝은 "모든 걸 다 좋게" 가 아니라 "무엇을 가장 중시할지 먼저 정하는" 일이에요.
비유하면 오디오 믹서 콘솔 같아요. 슬라이더가 잔뜩 있는데, 베이스를 올리면 다른 음역이 묻혀요. 좋은 엔지니어는 슬라이더를 다 올리지 않아요 — "이 곡은 보컬 중심" 이라고 먼저 정하고 거기에 맞춰 나머지를 양보시켜요. Kafka 튜닝도 똑같아요. 먼저 내 서비스가 처리량·지연·내구성·가용성 중 뭘 가장 중시하는지부터 정합니다.
네 가지 목표 — 동시에 다 잡을 수 없다
Kafka 성능 튜닝의 목표는 결국 네 가지로 정리돼요. 그리고 이들은 서로 당겨요.
| 목표 | 한 줄 | 주로 상충하는 것 |
|---|---|---|
| 처리량(Throughput) | 초당 많이 흘려보내기 | 지연 |
| 지연(Latency) | 한 건을 빨리 도착시키기 | 처리량 |
| 내구성(Durability) | 메시지를 잃지 않기 | 처리량·지연 |
| 가용성(Availability) | 일부 고장에도 안 멈추기 | 내구성 |
이 표가 튜닝의 지도예요. 아래부터는 "이 목표를 우선한다면 어느 노브를" 순서로 갈게요.
처리량 우선 — 모아서 크게 보낸다
처리량의 핵심 원리는 하나예요 — 작은 요청 여러 번보다 큰 요청 한 번이 싸다. 그래서 "모아서 크게"가 전략이에요.
- Producer —
batch.size를 키우고linger.ms를 조금 줘서(예: 5~50ms) 메시지를 모아 한 배치로 보내요.compression.type을lz4·zstd로 켜면 네트워크·디스크를 아껴 처리량이 또 올라가요.buffer.memory도 넉넉히. - Consumer —
fetch.min.bytes를 키워 "이만큼 쌓이면 가져와" 로 묶어 받고,max.poll.records도 늘려 한 번에 많이 처리해요. - Broker — 94편의
num.network.threads·num.io.threads가 코어 수에 맞게 충분한지 봐요.
여기서 시험 함정이 하나 있어요. linger.ms를 주면 처리량은 오르지만 그만큼 지연이 늘어요. 처리량과 지연은 같은 슬라이더의 양 끝이에요.
지연 우선 — 작게, 자주
실시간 알림·주문처럼 한 건이 빨리 도착해야 하면 정반대로 가요.
- Producer —
linger.ms=0(모으지 말고 즉시 전송),batch.size도 굳이 키우지 않아요. 메시지가 생기는 즉시 떠나게. - Consumer —
fetch.min.bytes=1로 "한 바이트라도 있으면 바로 줘",fetch.max.wait.ms도 짧게. - 트레이드오프 —
acks를all에서1로 낮추면 응답이 빨라져 지연이 주는데, 그만큼 내구성을 양보하는 거예요. 빨라진 만큼 잃을 위험을 받는 거죠.
내구성 우선 — 한 건도 잃지 않게
돈·주문·결제처럼 메시지를 절대 잃으면 안 되는 자리예요. 처리량·지연을 양보하고 안전을 챙겨요.
acks=all— 모든 ISR이 받았다고 확인해야 성공 처리. 89편 복제의 ISR과 직결돼요.min.insync.replicas=2이상 +replication.factor=3— 복제본을 충분히 두고, 동기화된 복제본이 최소 2개일 때만 쓰기 허용.enable.idempotence=true— 재시도 시 중복 저장 방지(멱등 producer).unclean.leader.election.enable=false— 뒤처진 복제본을 리더로 올리지 않음(올리면 데이터 손실).
이걸 다 켜면 안전한 대신 처리량·지연이 깎여요. 확인 단계가 늘어나니까요. 그래서 모든 토픽에 일괄 적용하지 말고, 정말 잃으면 안 되는 토픽에만 강하게 거는 게 실무 감각이에요.
가용성 우선 — 일부 고장에도 안 멈추게
여기서 정말 헷갈리는 트레이드오프가 나와요 — 내구성과 가용성은 한편 같지만 끝에서 부딪혀요.
min.insync.replicas를 높이면(내구성↑) 복제본이 한둘만 빠져도 쓰기가 거부돼 멈춰요(가용성↓).unclean.leader.election.enable=true로 두면 뒤처진 복제본이라도 리더로 올려 계속 굴러가요(가용성↑) — 대신 데이터 손실 위험(내구성↓).
정답은 서비스 성격이 정해요. 로그·이벤트 스트림처럼 조금 잃어도 멈추면 안 되는 자리는 가용성 쪽, 금융처럼 멈춰도 잃으면 안 되는 자리는 내구성 쪽이에요.
하드웨어·OS는 튜닝의 바닥
노브를 아무리 돌려도 바닥이 약하면 한계가 있어요. Kafka 성능 튜닝의 토대는 104편 Hardware·OS에 있어요. 핵심만 추리면 — Kafka는 OS 페이지 캐시에 크게 기대니 메모리가 넉넉해야 하고(JVM 힙은 오히려 작게), 디스크는 여러 개로 나눠 I/O를 분산하며, 네트워크 대역폭이 처리량의 실질적 천장이 돼요.
Kafka의 JVM 힙은 작게 두는 게 맞아요(보통 6GB 안팎). 남는 메모리는 OS가 페이지 캐시로 써서 디스크 읽기를 메모리 속도로 만들어 주거든요. 힙을 무작정 키우면 GC만 길어지고 캐시가 줄어 오히려 느려져요.
성능 튜닝 절차 — 측정 없이 만지지 마라
마지막이 가장 중요해요. 추측으로 노브를 돌리면 안 돼요. 절차는 한 줄이에요.
목표 정하기 → 현재 성능 측정(벤치마크) → 노브 하나만 변경 → 재측정 → 효과 확인 → 반복
kafka-producer-perf-test.sh·kafka-consumer-perf-test.sh 같은 기본 도구로 baseline을 먼저 재요. 그다음 한 번에 한 노브만 바꾸고 다시 재요. 여러 개를 동시에 바꾸면 무엇이 효과였는지 영영 알 수 없어요. 튜닝은 감이 아니라 측정이에요.
시험·면접 직전 압축 노트 — Kafka 성능 튜닝
- 성능 튜닝 = "다 좋게"가 아니라 무엇을 우선할지 먼저 정하기
- 네 목표 = 처리량 · 지연 · 내구성 · 가용성 (서로 상충)
- 처리량 ↔ 지연 = 같은 슬라이더의 양 끝
- 처리량 우선 = 모아서 크게 —
batch.size↑·linger.ms↑·compression·fetch.min.bytes↑·max.poll.records↑ - 지연 우선 = 작게 자주 —
linger.ms=0·fetch.min.bytes=1, 필요시acks=1(내구성 양보) - 내구성 우선 =
acks=all+min.insync.replicas≥2+replication.factor=3+enable.idempotence+unclean.leader.election=false - 내구성 강화는 처리량·지연 깎임 → 정말 중요한 토픽에만 강하게
- 가용성 vs 내구성 충돌 —
min.insync.replicas↑면 잘 멈춤(가용성↓),unclean.leader.election=true면 안 멈추나 손실 위험 - 선택 기준 = 조금 잃어도 안 멈춤(가용성) / 멈춰도 안 잃음(내구성)
- Kafka는 OS 페이지 캐시에 의존 → 메모리 넉넉히, JVM 힙은 작게(약 6GB)
- 디스크 여러 개로 I/O 분산, 네트워크 대역폭이 처리량 천장
- 튜닝 절차 = 목표 → 측정 → 한 번에 한 노브 → 재측정 → 반복
- 벤치마크 =
kafka-producer-perf-test.sh·kafka-consumer-perf-test.sh - 추측 튜닝·동시 변경 금지 — 무엇이 효과였는지 알 수 없게 됨
공식 문서: Apache Kafka — Hardware and OS와 Configuration에서 노브별 기본값과 권장 범위를 확인할 수 있어요.
시리즈 다른 편
같이 읽으면 좋은 글: