🗂️核心模型
Kafka 的"高吞吐"来自一个朴素设计:日志就是数组,追加即可,绝不随机改。
关键概念:Producer 按 key 哈希到 partition;Consumer Group 中每个 consumer 分配若干 partition;partition 数 = 最大并行消费度。
🔢Partition 与顺序保证
顺序的边界
Kafka 只保证单个 partition 内消息有序。跨 partition 不保序。
因此:要"同一订单顺序"(如创建→支付→发货),就把订单ID 作为 key,使其哈希到固定 partition,单线程消费该 partition 即可。
权衡:partition 越多 → 并行度越高、吞吐越大,但顺序粒度越粗(只能保证"同 key 顺序")。不要为了全局顺序而无脑用单 partition——那是反模式。
🪞副本机制与 ISR
每个 Partition 有多个副本(Replica),分布在不同 broker 上。其中一个是 Leader,其余是 Follower。
ISR(In-Sync Replicas,同步副本集合):与 Leader 保持"同步"的副本(落后不超过
replica.lag.time.max.ms)。只有 ISR 中的副本才有资格被选举为新 Leader。为什么有 ISR:避免"一个远远落后的 Follower 被提升为 Leader 后丢数据"。只有 ISR 成员能当 Leader,保证新 Leader 拥有全部已提交数据。
📏HW 与 LEO
这两个水位线决定了"消费者能读到哪、数据是否算提交",是 Kafka 不丢不重的核心。
| 概念 | 全称 | 含义 |
|---|---|---|
| LEO | Log End Offset | 每个副本日志的下一条写入位置(即当前最大 offset+1) |
| HW | High Watermark | ISR 中最小的 LEO,即"所有 ISR 都有的最后一条"——消费者只能读到 HW 之前 |
为什么用 HW:一条消息要被所有 ISR 副本都写入后,才对消费者"可见"(即推进 HW)。这样哪怕 Leader 宕机,新 Leader 也一定含有 HW 之前的数据,不会读到未提交/会丢失的消息。
经典坑:若按 LEO 对消费者可见,Leader 宕机后新 Leader 没这条 → 消息"消失"(幻读)。HW 机制正是为堵这个洞。0.11 后引入 Leader Epoch 进一步消除 HW 带来的边界问题。
🔄生产 / 消费流程
Producer 发送:按 key 选 partition(默认轮询或哈希),消息先攒在 batch,后台线程发送。
Leader 写入:Leader 把消息追加到本地 log,并更新 LEO。
Follower 同步:Follower 拉取 Leader 日志,写入本地,更新各自 LEO。
推进 HW:Leader 取 ISR 中最小 LEO 作为新 HW,消息对消费者可见。
Consumer 拉取:Consumer 按位移(offset)从 partition 拉取,处理完提交位移,下次从新位移继续。
acks 配置:
acks=0(不等确认,快但可能丢);acks=1(Leader 写入即确认,Leader 宕机可能丢);acks=all(ISR 全写才确认,最可靠)。可靠性要求高用 all。⚡零拷贝(Zero-Copy):吞吐之本
Kafka 消费时要把磁盘日志发给网络,传统路径要经过"内核→用户→内核"多次拷贝。Kafka 用 sendfile 系统调用跳过用户态。
传统 I/O:磁盘 → 内核页缓存 → 用户缓冲区 → socket 缓冲区 → 网卡(4 次拷贝 + 2 次上下文切换)。
零拷贝 sendfile:磁盘 → 内核页缓存 → 网卡(2 次拷贝,DMA 直达,CPU 几乎不参与)。
零拷贝 sendfile:磁盘 → 内核页缓存 → 网卡(2 次拷贝,DMA 直达,CPU 几乎不参与)。
为什么 Kafka 能扛百万/s:顺序磁盘写(磁盘顺序写比内存随机写还快)+ 零拷贝读 + 批处理(batch)+ 页缓存。四个武器叠加,吞吐远超传统 MQ。
🎯面试要点速记
模型:Topic 分多个 Partition,每个 Partition 是有序 append-only 日志,按 offset 编号。
顺序:仅 partition 内有序;同 key 哈希到同 partition 实现业务顺序。
副本:Leader 读写,Follower 拉同步;ISR=同步副本集合,仅 ISR 可当选 Leader。
HW/LEO:LEO=各副本末端;HW=ISR 最小 LEO,消费者只可见 HW 之前,防丢数据。
acks:0/1/all 三档,all 最可靠;配合 retries 防网络抖动丢消息。
零拷贝:sendfile 减少拷贝与上下文切换;+ 顺序写 + 批处理 = 高吞吐。
必考题:"Kafka 怎么保证不丢消息?"——生产端 acks=all+重试、服务端 多副本(ISR)+刷盘、消费端 先处理后提交 offset。再追问"顺序/高吞吐"就接 partition 与零拷贝。