1. 概览与导读
- 为何这样设计:统一处理高吞吐/低延迟/可容忍积压/分区派生/容错。
- 把磁盘当朋友:顺序 I/O + 页缓存;O(1) 日志结构胜过 B-Tree。
- 两大利器 :端到端批处理(MessageSet、批压缩)+ 零拷贝(
sendfile
)。 - 生产者:直连分区 Leader、语义分区、异步批。
- 消费者:拉模型 + 长轮询;offset 自主可回放;离线批;静态成员抗重平衡抖动。
- 语义:At-Most-Once / At-Least-Once / Exactly-Once(0.11+ 幂等 & 事务)。
- 复制 :ISR、
min.insync.replicas
、acks
权衡;非洁净选主的可用性/一致性取舍。 - 压缩:按 key 保留最后值(墓碑清理),适合 CDC、状态恢复、缓存回填。
- 配额:按 (user, client-id) 限流(字节率/线程占用),保障多租户公平。
2. 动机(Motivation)
Kafka 被设计为统一的实时数据平台:要高吞吐、能承载积压、低延迟、支持分区与派生流,并在故障下保持容错。整体更像数据库日志 :顺序追加、可回放、有序分区。
3. 持久化(Persistence)
3.1 顺序 I/O 胜过随机 I/O
顺序写吞吐可达数百 MB/s,而随机写由寻道主导(毫秒级),差距可达数千倍。OS 利用 read-ahead/write-behind 将小 I/O 合并为大块顺序 I/O。
3.2 Page Cache 与 JVM 现实
OS 会把空闲内存用于页缓存;进程内缓存常与 page cache 重复 。JVM 对象开销与 GC 放大明显。采用紧凑字节结构 + page cache 可获得接近物理内存大小的热缓存 且重启仍热。
3.3 写入即持久(进入内核页缓存)
到达即写入文件系统日志(转入内核 page cache),把缓存一致性逻辑交给 OS,简化实现。
3.4 O(1) 的日志式结构
日志式"追加写 + 顺序读"使操作近似 O(1),读不阻塞写,性能与数据量解耦;允许较长时间保留消息,提升消费者弹性。
4. 效率(Efficiency)
4.1 小 I/O 与字节拷贝
协议围绕 MessageSet 设计,端到端批处理 ,降低往返与小 I/O;Producer/Broker/Consumer 共享统一二进制格式,减少拷贝。
4.2 零拷贝 sendfile
常规路径有 4 次拷贝与 2 次系统调用;sendfile
让 page cache 直接喂 socket,仅剩最终进 NIC 的一次拷贝。启用 SSL 时不走 sendfile。
4.3 端到端批压缩
冗余跨消息更多,批压缩 效果显著;批在磁盘与网络 全路径保持压缩态。支持 GZIP/Snappy/LZ4/Zstd。
5. 生产者(Producer)
直连分区 Leader(所有 Broker 可提供元数据);语义分区 (按 key,如 userId);异步批(如 64KB 或 10ms)用少量延迟换吞吐激增。
6. 消费者(Consumer)
6.1 拉 vs 推
拉模型更能适配不同速率(慢就落后);天然适配大批量 ;用长轮询避免空转。
6.2 位置管理与可回放
组内"每分区仅一消费者",已消费状态就是一个offset ;支持回退重放。
6.3 离线批加载
Hadoop 等可按"节点/主题/分区"并行导入,失败任务从原 offset 重启,无重复风险。
6.4 静态成员(KIP-345)
为每实例设唯一 group.instance.id
(2.3+),重启不触发重平衡,避免大规模状态恢复抖动;重复 ID 会被 Broker 围栏。
7. 消息投递语义
- 至多一次:可能丢、不重复(先提交 offset 再处理;Producer 关重试)。
- 至少一次:不丢、可能重复(先处理再提交 offset;依赖幂等写)。
- 恰好一次 :Kafka→Kafka 链路用事务生产者 +
read_committed
消费者(0.11+ 幂等与事务支持);写外部系统需与目标系统协作(一起持久化 offset 与输出)。
8. 使用事务
三要点:
- 分区分配确保组内每分区仅一消费者;
- 生产者用事务把"输出记录 + offset 更新"原子提交;
- 建议一消费者配一生产者以简化与重平衡的交互。
常用配置:
properties
# Consumer
isolation.level=read_committed
enable.auto.commit=false
# Producer
transactional.id=your-unique-tx-id
enable.idempotence=true
acks=all
异常/中止后可"重建 Producer/Consumer"或用 seek()
回退。
9. 复制(Replication)
9.1 基本模型与 controller
每分区 1 Leader + 若干 Follower;Follower 像消费者一样从 Leader 拉并批量写入;controller 负责 Broker 注册与批量选主。
9.2 提交与消费者可见性
提交(committed) :当且仅当 ISR 全部副本写入;消费者可见需同时满足:
- 复制到 ISR 全部副本;
- ISR 数量 ≥
min.insync.replicas
。
acks
只影响生产者等待策略,不改变消费者可见性前提。
9.3 多数派 vs ISR
多数派(Raft/Zab)延迟由最快副本决定但成本高;Kafka 的 ISR 动态维护"追齐集合",在同等容错目标下写放大与空间开销更低。
9.4 非洁净选主
所有副本都挂时:
- 等 ISR 恢复 → 一致性优先、可能长期不可用;
- 谁先活谁当 Leader → 可用性优先、可能丢提交数据。
0.11+ 默认等待 ISR ,unclean.leader.election.enable
可切换策略。
9.5 可用性与持久性
acks=all
等待的是当前 ISR ;复制因子=2 且 ISR=1 时仍会成功,但若仅存副本也宕机则可能丢失 。提升持久性:禁用非洁净选主 + 设置 min.insync.replicas
。
9.6 领导权均衡与快速切换
分区与领导权均衡分布 ;controller 批量通知领导权变更,缩短不可用窗口;controller 故障可再选。
10. 日志压缩(Log Compaction)
按 key 至少保留最后值,适用于 CDC、事件溯源、状态恢复与缓存回填。
- 被删除用墓碑(key 有值、payload=null)表示;墓碑在
delete.retention.ms
后自身清理。 - 顺序不变 、offset 永不改变;被压缩掉的 offset 与其后存在的 offset 等价。
min/max.compaction.lag.ms
约束"多久内不会被压;最多多久后必须可被压"。
11. 配额(Quotas)
- 网络带宽配额:按字节率(bytes/sec)。
- 请求速率配额:按 I/O+网络线程时间占比(近似 CPU 占用)。
- 作用域按 (user, client-id) / user / client-id,优先级由具体到泛化。
- 违规时 Broker 计算延迟 、静音通道 并形成反压;用多个小时间窗(如 30×1s)快速发现与矫正。
12. 实战参数与场景速查
properties
# Producer(吞吐/可靠)
batch.size=65536
linger.ms=10
compression.type=lz4 # gzip/snappy/lz4/zstd
acks=all
enable.idempotence=true
# Consumer(批/事务)
fetch.min.bytes=1048576
fetch.max.wait.ms=500
isolation.level=read_committed
enable.auto.commit=false
# 复制与一致性(Broker/Topic)
min.insync.replicas=2
unclean.leader.election.enable=false
# 主题级日志压缩
log.cleanup.policy=compact
min.cleanable.dirty.ratio=0.1
log.cleaner.min.compaction.lag.ms=60000
log.cleaner.max.compaction.lag.ms=3600000
delete.retention.ms=86400000
13. 常见坑与工程建议
-
SSL 与 sendfile :启用 TLS 不走零拷贝,跨 IDC/多租户链路更要开启批压缩。
-
acks 与 ISR :
acks=all
依赖当前 ISR 数 ;结合min.insync.replicas
才能在副本减少时形成背压与保护。 -
语义=顺序:
- 先提交 offset 再处理 → 至多一次;
- 先处理后提交 offset → 至少一次;
- 事务产出 + 同事务写 offset +
read_committed
→ 恰好一次(Kafka→Kafka)。
-
静态成员 :为每实例设唯一
group.instance.id
,避免运维重启引发"任务大洗牌"。 -
Compaction ≠ 完整历史:它保证"最终值",有完整历史需求需时间/大小保留或另存变更日志。
-
Cleaner 限速与监控 :注意
uncleanable-partitions-count
、max-clean-time-secs
、max-compaction-delay-secs
。
14. 结语
Kafka 以日志式存储 + 页缓存 + 顺序 I/O + 批处理/零拷贝 在吞吐、成本与可运维性间取得平衡;配合幂等/事务、ISR 复制、日志压缩、配额 ,可按场景在一致性、可用性与性能 之间灵活调参。
落地路线 :先用默认(批 + 压缩 + acks=all
)→ 按丢/重语义调整 offset/事务 → 配置 min.insync.replicas
/选主策略 → 需要时启用 compaction → 多租户生产后加配额与监控。