Kafka는 큐가 아니다 — 영속적 분산 로그가 만든 모든 차이
Kafka를 처음 배우는 사람 대부분은 "메시지 큐"라는 설명을 듣고 RabbitMQ처럼 생각한다. 그래서 "소비했는데 왜 데이터가 남지?", "왜 메시지 순서가 섞이지?" 같은 오해에 빠진다. 이 오해의 뿌리는 단 하나다 — Kafka는 소비하면 사라지는 큐가 아니라, 영속적이고 파티션 내 순서가 보장되는 분산 로그다. 이 하나의 차이가 Kafka의 모든 설계(복제, 컨슈머 그룹, retention, 전달 의미론)를 설명한다.
이 글은 그 핵심 추상 — partition = append-only log — 을 중심으로 Kafka가 무엇인지, 왜 그렇게 설계됐는지, 그리고 왜 "빠르다"는 평가가 마법이 아니라 설계의 결과인지를 다룬다. retention·복제·rebalance·exactly-once가 암기 과목이 되지 않으려면, 이 로그 모델을 먼저 정확히 잡아야 한다.
Kafka는 영속적 분산 로그다
정확히 말해, Kafka의 partition 하나는 끝에만 줄을 적는 장부(append-only ledger)다. 이미 적힌 줄은 번호(offset)가 매겨지고 고정된다. 읽는 사람(consumer)은 자기가 마지막으로 읽은 줄 번호를 기억해 두고, 다음 번호부터 다시 읽는다. 여러 사람이 같은 장부의 같은 번호를 여러 번 읽을 수 있다 — 장부는 읽었다고 지워지지 않는다.
flowchart LR
P[producer] -->|write| T["topic<br/>(multi-producer, multi-consumer)"]
T --> PART["partition 0<br/>partition 1<br/>partition 2<br/>(각각 append-only log)"]
PART -->|read| C1[consumer A]
PART -->|read| C2[consumer B]
ASF 공식 문서가 강조하는 핵심 사실들: (introduction.md)
- topic은 multi-producer, multi-consumer다. 여러 클라이언트가 같은 topic에 동시에 쓰고 읽는다.
- 소비해도 삭제되지 않는다. 전통 메시징과 달리 메시지는 consumer가 읽었다고 사라지지 않는다. 대신 topic별 retention(시간/크기) 정책으로만 삭제된다. → 같은 데이터를 여러 consumer 그룹이 독립적으로, 그리고 나중에 다시(replay) 읽을 수 있는 이유.
- 같은 key의 메시지는 같은 partition에, partition 내에서는 쓴 순서대로 읽힌다. (전체 topic 순서 보장은 아니다.)
- 데이터 크기에 관계없이 성능이 일정하다 — O(1) disk seek per read, 순차 I/O 덕분. 이것이 장기 보관을 가능하게 한다.
record — 데이터 단위의 구조
Kafka의 데이터 단위는 record(event/message)다. 1차 출처 message-format.md에 따른 구조:
| 필드 | 설명 | 비고 |
|---|---|---|
key |
파티셔닝 키(선택). 같은 key → 같은 partition | null 가능 → sticky partitioning |
value |
실제 페이로드 | 바이트 배열. 형식 자유(JSON, Avro 등) |
timestamp |
메시지 시각 | CreateTime(producer 지정) 또는 LogAppendTime(broker 지정). topic 설정 message.timestamp.type |
headers |
키-값 메타데이터 | KIP-82, Kafka 0.11(v2 포맷)에 추가 |
key가 없으면(null) producer는 sticky partitioning을 쓴다 — 한 batch의 메시지를 같은 partition으로 몰아 넣어 효율을 높인다. key가 있으면 murmur2(key) % numPartitions로 partition이 결정된다(자세한 건 02장).
큐가 아니라 로그라는 게, 구체적으로 뭐가 다른가
"Kafka는 큐가 아니다"라는 말이 추상적이다. 전통 메시징(RabbitMQ 같은 AMQP 브로커)과 비교하면 차이가 선명해진다.
| 항목 | 전통 메시징(큐) | Kafka(로그) |
|---|---|---|
| 소비 후 | 즉시 삭제 | 남아있음(retention이 지나야 삭제) |
| 같은 메시지 재소비 | 불가(지워졌으므로) | 가능(offset만 다시 돌리면 replay) |
| 여러 소비자가 같은 데이터 | 큐는 경쟁 소비(한 메시지=한 소비자) | consumer group마다 독립적으로 전부 읽기 가능 |
| 순서 보장 단위 | 큐 전체(보통) | partition 내만 |
| 처리량 한계 | 메모리/인덱스 한계, 삭제 비용 | 디스크 순차 I/O → 대량 장기 보관에 유리 |
핵심 차이는 "소비 = 삭제" 모델이냐 "소비 = 읽기 커서 이동" 모델이냐다. RabbitMQ에서 consumer가 메시지를 ack 하면 그 메시지는 사라진다. Kafka에서 consumer가 메시지를 읽으면 broker는 아무것도 하지 않는다 — consumer가 자기 offset을 어디까지 읽었나 기억할 뿐이다. 그래서 같은 topic을 이벤트 소싱용으로도, 분석용으로도, 나중에 재처리용으로도 동시에 읽을 수 있다. 이 "읽기가 파괴적이지 않다"는 성질이 Kafka를 단순 메시지 브로커가 아니라 이벤트 스트리밍 플랫폼으로 만든다.
왜 로그인가 — Kafka가 빠른 진짜 이유
"Kafka가 빠르다"는 평가는 마법이 아니라 세 가지 설계 결정의 결과다: 순차 디스크 I/O + 일괄(batch) + zero-copy.
순차 디스크 I/O가 무작위 메모리 접근보다 빠를 수 있다
직관과 달리, 동일한 디스크에서 순차 쓰기는 무작위 읽기보다 훨씬 빠르다. Kafka는 이를 극단까지 밀어붙인다 — 모든 쓰기는 로그 끝에 append, 읽기도 offset 기준 순차 탐색. (design.md)
순차 vs 무작위 디스크 접근: 같은 디스크에서 순차가 수십~수백 배 빠르다. 7200rpm HDD 기준 순차 읽기 ~100 MB/s, 무작위 읽기는 수백 KB/s 수준. SSD는 격차가 좁지만 여전히 순차가 유리하다. (수치는 미검증, design.md의 상대적 통찰에 근거)
데이터 크기가 커져도 읽기 비용이 일정한 이유(O(1) per read)가 바로 이것이다 — offset으로 위치를 계산해 순차로 읽기 때문에, 1TB든 1GB든 읽기 단가가 같다. retention 비용이 "디스크 용량만 있으면 무한 보관 가능"이 되는 이유이기도 하다.
batch + 압축의 종단간 전달
producer는 메시지 여러 개를 한 묶음(batch)으로 모은다 — linger.ms/batch.size까지 모아 한 번에 전송해 네트워크 왕복을 최소화한다. 여기에 batch compression이 더해진다:
- producer가 batch를 압축해서 broker로 보낸다.
- broker는 검증을 위해 잠깐 해제하지만, 디스크엔 압축된 채 저장하고 consumer에게도 압축된 채 전송한다.
- 네트워크 비용이 종단간 절감된다.
zero-copy — 커널 페이지 캐시에서 소켓으로 곧장
보통 파일을 읽어 네트워크로 보내려면: 디스크 → 커널 페이지 캐시 → 사용자 공간(응용 메모리) → 커널 소켓 버퍼 → 네트워크 카드. 사용자 공간을 거치는 복사가 두 번 든다. Kafka는 sendfile(zero-copy) 로 이 복사를 건너뛴다 — 데이터가 커널 페이지 캐시에서 소켓으로 곧장 간다.
flowchart LR
subgraph 일반["일반 전송(복사 4단계)"]
D1[디스크] --> PC1[페이지 캐시] --> US1[사용자 공간] --> SK1[소켓 버퍼] --> N1[NIC]
end
subgraph ZC["Kafka zero-copy (sendfile)"]
D2[디스크] --> PC2[페이지 캐시] --> SK2[소켓 버퍼] --> N2[NIC]
end
게다가 Kafka는 디스크 파일을 커널 페이지 캐시에 올려두고 그대로 소비한다 — 별도의 응용 캐시를 두지 않는다. 그래서 남는 메모리가 곧 읽기 캐시가 된다. "Kafka는 메모리를 적게 쓰면서도 빠르다"는 평가의 진짜 이유.
절대 처리량(예: "초당 수백만 건")은 하드웨어·메시지 크기에 따라 폭넓게 변동한다. 공식 design 문서는 상대적 통찰(순차 » 무작위, batch/zero-copy의 효과)만 제시한다. 구체 수치는 Confluent 벤치마크(2차 출처) 참조 시 출처 명시 필수.
partition — 병렬성과 순서의 트레이드오프
단일 로그는 단순하지만 병렬성이 없다. Kafka는 topic을 partition으로 쪼개 이 문제를 푼다. 각 partition이 독립된 병렬 단위가 되어 쓰기도 분산되고 consumer도 분산된다. 대신 전체 순서는 포기하고 partition 내 순서만 보장한다는 트레이드오프를 치른다.
- partition 내: 쓴 순서대로 읽힌다(단일 로그의 순서 보장).
- partition 간: 순서 보장 없음.
- 전체 topic 순서가 꼭 필요하면 partition=1로 둬야 한다 — 병렬성을 포기하는代价로.
partition 수는 어떻게 정하나
이게 자주 묻는 질문이고, 정답은 "상황에 따라"다. trade-off를 보자:
| partition을 늘리면 | 대가 |
|---|---|
| 병렬성↑(consumer 동시 처리↑) | broker가 관리할 partition 파일·리더 수↑(파일 핸들·메모리) |
| 처리량↑ | rebalance 비용↑(partition이 많을수록 재분배가 느림) |
| 리더 선출·복제 부하↑ |
공식 문서는 단일 정답을 주지 않는다 — 목표 처리량·컨슈머 수·클러스터 규모로 산정한다. (구체 권장값은 없으니 "미겕안" 표식 없이 단정하지 말 것.) 일반적으로 "당장 필요한 것보다 약간 여유"로 시작하고, 모니터링하면 늘린다. partition 수를 줄이는 것은 지원되지 않는다는 것도 기억하라 — 늘리기만 가능하다(02장 상세).
produce 경로 — 메시지가 broker에 도달하기까지
producer가 send(record)를 부르면 메시지가 즉시 broker로 가는 게 아니다. accumulator에 쌓였다가 batch로 묶여 간다.
sequenceDiagram
participant P as producer
participant ACC as accumulator(batch)
participant L as partition leader broker
participant F as ISR follower brokers
P->>ACC: send(record) — 직렬화+파티셔닝 후 batch 적재
ACC->>L: batch 전송(압축)
L->>L: 로그 끝에 append
L->>F: 복제(팔로워 fetch)
F-->>L: ACK (HW 갱신)
L-->>P: ack (acks 설정에 따라)
| 단계 | 일어나는 일 | 비고 |
|---|---|---|
| 파티셔닝 | key 있으면 toPositive(murmur2(key)) % numPartitions로 partition 결정. key 없으면 sticky |
BuiltInPartitioner.java. murmur2, consistent hash 아님 |
| batch 적재 | linger.ms/batch.size까지 모아 한 번에 전송 |
네트워크 왕복 최소화 |
| leader append | partition 리더 broker가 로그 끝에 기록 | 순차 I/O |
| 복제 | ISR 팔로워들이 리더에서 fetch해 복제 | 장 06 상세 |
| ack | acks 설정(0/1/all)에 따라 producer에게 확인 |
장 03/08 상세 |
acks — "얼마나 확실히 썼다고 볼 것인가"
producer가 메시지를 보낸 뒤 broker에게 확인(ack)을 기다리는 강도를 acks가 결정한다. 이 한 설정이 성능과 신뢰성의 균형을 잡는다.
acks |
의미 | 잃을 수 있는 것 | 대표 용도 |
|---|---|---|---|
0 |
broker 응답 안 기다림(fire and forget) | broker가 받기도 전에 네트워크에서 사라지면 유실 | 로그·메트릭 등 손실 감내 가능한 고처리량 |
1 |
리더만 확인(리더 장애 직전에 받고 복제 전이면 유실 가능) | 리더 장애 시 미복제 메시지 유실 | 성능과 신뢰성 절충 |
all(-1) |
ISR 전원 확인(복제까지 끝나야 성공) | ISR 전원 장애가 아니면 유실 없음 | 신뢰성 최우선(금융·주문) |
acks=all이 "가장 안전"이지만 단독으론 부족하다 — ISR이 1개만 남으면 그 1개만 확인하면 성공이므로 사실상 신뢰성이 없다. 그래서 min.insync.replicas(예: 2)와 세트로 써야 ISR 미달 시 쓰기를 거부하게 만든다. 이 조합(replication.factor=3, min.insync.replicas=2, acks=all)이 프로덕션 표준이다(장 06·08 상세).
idempotent producer(
enable.idempotence=true)는acks=all을 자동 강제한다 — 재시도 중 중복을 막으려면 복제 완료가 보장돼야 하므로. 이 설정 관계는 장 08(exactly-once)의 핵심이다.
consume 경로 — consumer가 데이터를 당기는 방식
여기서 자주 틀린다. "producer가 쓰면 consumer에게 push된다"가 아니다. consumer가 능동적으로 poll()로 당겨온다. broker는 consumer가 요청하지 않으면 아무것도 보내지 않는다.
sequenceDiagram
participant C as consumer
participant L as partition leader broker
participant OS as __consumer_offsets
C->>C: poll() 호출
C->>L: Fetch 요청(이전 offset부터)
L-->>C: batch 반환(압축 해제)
C->>C: 애플리케이션 처리
C->>OS: offset commit(자동/수동)
- consumer는 poll loop로 당긴다. 한 번에 여러 메시지 batch가 반환된다.
- offset은
__consumer_offsets내부 topic에 저장된다(0.9부터 ZooKeeper에서 이관). 장 04 상세. - 같은 offset을 여러 번 읽을 수 있다(replay). consumer가 자기 offset만 기억하면 된다.
왜 pull인가
broker가 consumer에게 push하는 대신 consumer가 당기게 한 이유는 세 가지다 (design.md: Push vs. pull):
- consumer가 자기 속도를 스스로 제어 → 압도당하지 않는다(backpressure가 자연스럽다).
- broker가 consumer 상태를 추적할 부담이 줄어든다.
- replay(과거 offset부터 다시 읽기)가 자연스럽다 — push 모델에선 불가능하거나 어렵다.
offset — consumer의 읽기 커서, 그리고 로그가 사라지는 조건
offset은 partition 내 각 메시지의 단조증가 번호다. consumer는 "어디까지 읽었나"를 offset으로 기억하고, 다음 poll부터 그 다음 offset을 요청한다. 두 가지를 짚고 넘어가야 한다.
offset commit — consumer가 읽은 위치를 영속화하는 행위. 자동(enable.auto.commit=true, poll 주기마다)과 수동(애플리케이션이 처리 완료 후 명시 commit)이 있다. 자동이 편하지만 함정이 있다 — "읽고 commit 했는데 처리가 실패하면 그 메시지는 영영 사라진다(최소 한 번 at-least-once 위반 방향)". 신뢰성이 중요하면 수동 commit이 필요하다(장 04·08).
retention — 로그가 사라지는 조건. consumer가 읽든 안 읽든 상관없이, 메시지는 retention 정책이 도달하면 삭제된다:
retention.ms(시간, 기본 168시간=7일) 또는retention.bytes(크기) 중 먼저 도달한 쪽이 삭제를 발동시킨다.- 둘 다 설정하면 OR가 아니라 "먼저 닿은 쪽"이다 — 이걸 단일 조건으로 오해하면 보관 기간 추정이 틀린다.
retention은 장 07에서, 정확한 전달(수동 commit·exactly-once)은 장 08에서 다룬다. 여기선 "소비해도 지워지지 않되, retention이 닿으면 지워진다"는 두 축만 기억하라.
왜 이렇게 설계됐나 — 네 가지 핵심 설계 결정
지금까지의 모든 것이 네 가지 질문에 대한 답으로 요약된다.
- 왜 로그인가? 로그는 append·순차읽기에 최적화된 가장 단순한 자료구조다. 복제·재생산(replay)·순서 보장이 모두 자연스럽다.
- 왜 partition인가? 단일 로그는 병렬성이 없다. partition으로 쪼개면 각 partition이 독립된 병렬 단위가 된다(쓰기도 분산, consumer도 분산). 대신 전체 순서를 포기하고 partition 내 순서만 보장한다는 트레이드오프.
- 왜 consumer group인가? 한 partition을 여러 consumer가 동시에 읽으면 순서가 꼬인다. 그래서 한 consumer group 내에서 각 partition은 정확히 한 consumer가 소유한다. consumer가 늘어나면 partition이 재분배(rebalance). 장 05 상세.
- 왜 pull인가? consumer가 속도를 스스로 제어하고, broker 부담을 줄이고, replay를 자연스럽게.
이 네 가지는 독립이 아니라 서로를 강화한다. 로그이기 때문에 partition으로 쪼개도 순서가 partition 내에 보존되고, partition이 병렬 단위이기 때문에 consumer group이 partition을 소유하는 모델이 말이 되며, pull이기 때문에 consumer group마다 자기 offset·속도로 독립 읽기가 가능하다. 하나를 빼면 전체가 흔들린다 — 그래서 Kafka를 "빠른 큐"로만 보면 이 네 기둥의 시너지를 놓친다.
ZK는 사라졌다 — KRaft 시대
Kafka 4.0(2025-03)부터 ZooKeeper 모드가 완전히 제거되고 KRaft만 남았다. 메타데이터(어떤 topic, 어느 partition의 리더가 누구, ISR은 누구)를 더 이상 외부 코디네이터(ZK)에 맡기지 않고, broker들 사이에서 Raft 합의로 스스로 관리한다.
왜 ZK를 버렸나 — 메타데이터 병목
ZK 시절 Kafka는 두 시스템을 운영해야 했다: 메시지를 다루는 broker와 메타데이터를 다루는 ZK 앙상블. 이중 운영은 그 자체로 부담이었지만, 진짜 문제는 확장성이었다. partition 수가 늘어날수록 broker가 ZK에 갱신·구독해야 할 메타데이터가 선형으로 늘어났고, 수만 partition 규모에서 controller(ZK와 통신하는 특별 broker)가 병목이 됐다. controller 장애 시 메타데이터를 새 controller가 다시 로드하는 데 수분이 걸리는 일도 있었다.
KRaft는 이 메타데이터 자체를 Kafka의 로그 구조에 담아 Raft로 복제한다. 그래서 메타데이터 갱신이 단일 Raft 로그로 선형화(linearizable)되고, controller 장애 시에도 다른 controller가 즉시 이어받는다(수분이 아니라 수초). "Kafka로 Kafka의 메타데이터를 관리한다"는 자기참조적 설계다.
- 역할 분리: 각 서버는
process.roles로broker,controller, 또는broker,controller(combined) 중 하나. 단일 노드 학습은 combined. - controller quorum: controller들이 Raft로 활성 컨트롤러를 선출. 과반수(quorum)가 살아있으면 메타데이터 일관성 유지.
- 이득: 외부 의존성(ZK) 제거, 메타데이터 갱신 선형화, 대규모 클러스터(partition 수만 개)에서 확장 용이, controller 장애 복구 시간 단축.
단일 노드 vs 프로덕션 토폴로지
이 글(와 이 서브프로젝트 전체)의 실습은 combined 모드 단일 노드다 — 한 프로세스가 broker와 controller 역할을 모두 한다. 학습엔 충분하지만 내결함성은 없다(그 프로세스가 죽으면 클러스터 끝). 프로덕션 표준 토폴로지는:
- broker
replication.factor=3(장 06) — 1 broker 장애까지 데이터 손실 없이 감내. - controller 3개(quorum) — 1개 장애까지 감내(과반수 2 유지).
- 역할은 분리(controller-only 노드 + broker-only 노드)하거나 combined하거나, 규모에 따라 선택.
단일 노드 환경에선 복제·ISR·leader 선출이 의미 없다. 복제 실습은 최소 3 broker 환경이 필요하다(장 06·gotchas.md).
ZK 시절(3.x 이하)은 역사적 배경으로만. 4.x 자료가 아니면
--zookeeper·zookeeper.connect·config/kraft/같은 명령/설정이 등장하는데, 이것들은 4.x에서 동작하지 않는다. (장 12 운영·gotchas.md 참조)
core 위에 생태계가 올라간다 — 범위 정리
지금까지 본 broker·topic·partition·producer·consumer가 Apache Kafka core(ASF 산하)다. 이 로그 모델 위에 몇 가지가 더 올라간다:
- Kafka Connect(ASF core): source/sink 커넥터로 DB·파일 ↔ Kafka 이동. 장 10.
- Kafka Streams(ASF core): topic 간 스트림 처리 라이브러리(topology·state store). 장 11.
- Schema Registry(Confluent 제공, ASF core 아님): Avro/JSON/Protobuf 스키마를 저장·호환성 검사. 장 09. core에 포함되지 않으므로 별도 설치·별도 문서.
이 서브프로젝트는 core를 중심으로 하되 생태계는 "어떤 문제를 푸는가·core와 어디서 만나는가"로 다룬다. core 사실과 Confluent 확장 사실을 섞지 않는 게 핵심 규칙이다.
Kafka 4.3 실습 — 직접 확인하기
사전: KRaft 단일 노드 기동(
harness/commands.md또는kafka-verify스킬). 단일 노드라 복제 동작은 일부만 관찰된다.
topic이 "삭제되지 않는 로그"임 확인
# Kafka 4.3, KRaft 단일 노드
bin/kafka-topics.sh --create --topic arch-demo --partitions 3 --replication-factor 1 --bootstrap-server localhost:9092
bin/kafka-console-producer.sh --topic arch-demo --bootstrap-server localhost:9092
# (콘솔에 메시지 몇 줄 입력 후 Ctrl-D)
확인할 것: producer가 종료돼도 메시지가 broker에 남아 있다(로그 모델).
bin/kafka-console-consumer.sh --topic arch-demo --from-beginning --bootstrap-server localhost:9092
# 같은 메시지를 처음부터 다시 읽을 수 있다(replay)
partition 분산과 offset 확인
bin/kafka-topics.sh --describe --topic arch-demo --bootstrap-server localhost:9092
확인할 것: Partition: 0/1/2, 각 partition의 Leader, Replicas, Isr.
Topic: arch-demo PartitionCount: 3 ReplicationFactor: 1
Topic: arch-demo Partition: 0 Leader: 1 Replicas: 1 Isr: 1
Topic: arch-demo Partition: 1 Leader: 1 Replicas: 1 Isr: 1
Topic: arch-demo Partition: 2 Leader: 1 Replicas: 1 Isr: 1
key 없이 produce하면 sticky로 한 partition에 몰릴 수 있어 위 describe만으로는 분산이 안 보일 수 있다. key를 주고 produce하면
murmur2(key) % 3로 고정 partition이 결정되는지 교차 확인하라.
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "Kafka는 큐라서 소비하면 사라진다" | 로그다. 소비해도 retention이 지나기 전엔 남는다. 그래서 replay·여러 consumer 그룹 독립 읽기 가능 |
| "Kafka는 전체 순서를 보장한다" | partition 내 순서만. 여러 partition 섞으면 순서 보장 없음. 전체 순서 필요하면 partition=1(병렬성 포기) |
| "Kafka는 실시간 push다" | pull 기반. consumer가 poll()로 당긴다. broker가 밀어넣지 않는다 |
| "key만 주면 순서 보장 + 병렬 처리 둘 다" | 같은 key는 같은 partition → 순서 보장. 하지만 key 종류가 적으면 partition이 쏠려 병렬성이 떨어짐(cardinality 트레이드오프) |
| "partition은 줄일 수 있다" | 줄일 수 없다. 늘리기만 가능(02장) |
| "Kafka가 빠른 건 메모리 DB라서" | 아니다. 디스크 기반 로그 + 순차 I/O + zero-copy + batch 덕분 |
| "ZK 기반 자료 그대로 써도 된다" | 4.x에선 --zookeeper 동작 안 함. 무조건 --bootstrap-server |
요약 — 이 글의 결론
- Kafka = 분산 커밋 로그. 소비해도 retention 전엔 남는다(재생산·다중 소비 가능). 이게 "큐가 아닌 로그"라는 한 문장이 모든 설계를 설명한다.
- partition = append-only log + 순서 보장 단위 + 병렬 단위. 전체 순서 포기, partition 내 순서 보장. partition은 늘릴 수만 있고 줄일 수 없다.
- record =
key+value+timestamp+headers(0.11+). 같은 key →murmur2(key) % numPartitions(consistent hash 아님). - producer 쓰기: batch → leader append → ISR 복제 → ack(
acks). 압축은 종단간. - consumer 읽기: pull(
poll()) → offset 기반 →__consumer_offsets에 commit. replay 가능. - 빠른 이유: 순차 I/O + batch 압축 + zero-copy(
sendfile). 마법 아님 — 페이지 캐시를 읽기 캐시로 쓴다. - KRaft: ZK 제거(4.0), broker들이 Raft로 메타데이터 자체 관리. ZK 시절 명령/설정은 4.x에서 동작 안 함.
- 설계의 네 기둥: 로그(단순+순차) · partition(병렬, 순서 트레이드오프) · consumer group(한 partition=한 consumer) · pull(속도 제어+replay).
생각해 볼 문제
- Kafka가 "큐가 아니라 로그"라는 말의 의미를, retention 관점에서 설명하라.
- 같은 key의 메시지가 어떻게 같은 partition에 도달하는지, 파티셔너 알고리즘(murmur2)을 포함해 설명하라.
- consumer가 broker에 데이터를 "요청"한다는 것이 왜 설계상 유리한가(pull vs push)?
- partition을 늘리면 얻는 것과 잃는 것을 각각 말하라. 왜 줄일 수 없는가?
- Kafka가 디스크 기반인데 빠른 이유를 세 가지 설계 결정으로 설명하라.
- zero-copy(sendfile)가 없었다면 consumer 읽기 경로에서 구체적으로 어떤 비용이 추가되는가?
- KRaft가 ZooKeeper를 대체한 핵심 이유 하나를 말하라.
참고
- Introduction — Main Concepts - 접근 2026-07-09
- Design — Motivation & Persistence - 접근 2026-07-09 (순차 I/O, zero-copy, push vs pull, batch compression)
- Record message format - 접근 2026-07-09
- BuiltInPartitioner.java (murmur2) - 접근 2026-07-09
- KRaft — operations/kraft.md - 접근 2026-07-09
- KIP-833(KRaft production ready), KIP-853(동적 controller quorum), KIP-82(record headers)
'Tech Artifacts > Kafka' 카테고리의 다른 글
| Kafka - 06. replication (0) | 2026.07.09 |
|---|---|
| Kafka - 05. consumer group (0) | 2026.07.09 |
| Kafka - 04. consumer (0) | 2026.07.09 |
| Kafka - 03. producer (0) | 2026.07.09 |
| Kafka - 02. topic and partition (0) | 2026.07.09 |
