一句话总结
Kafka 核心设计:分区(Partition,并行+扩展的基础)、副本(Replica,高可用的基础)、ISR(In-Sync Replicas,与 Leader 保持同步的副本集合)。关键概念:HW(High Watermark,消费者可见的最高 offset)、LEO(Log End Offset,日志末尾 offset)。高性能秘诀:顺序写磁盘(日志追加)、零拷贝(sendfile 系统调用,数据不经过用户态)、Page Cache(利用操作系统缓存)、批量压缩。
初级理解
Kafka 架构
# Kafka 核心组件
# Broker:Kafka 服务器节点
# Topic:消息主题(逻辑概念)
# Partition:分区(物理概念,Topic 的子集)
# Replica:副本(分区的备份)
# Producer:生产者
# Consumer:消费者
# Consumer Group:消费者组
# Zookeeper/KRaft:元数据管理(Controller 选举)
# 分区的作用
# 1. 并行处理:多个分区可并行读写
# 2. 水平扩展:分区分布在不同 Broker 上
# 3. 提高吞吐:分区越多,并行度越高
# 副本的作用
# 1. 高可用:Leader 宕机,Follower 提升为 Leader
# 2. 数据冗余:每个分区有多个副本
中级深入
ISR 机制
# ISR(In-Sync Replicas):与 Leader 保持同步的副本集合
# Leader 在 ISR 中,所有 ISR 副本都同步了消息
# 副本加入 ISR 条件
# Follower 与 Leader 的 LEO 差距 < replica.lag.time.max.ms(默认 30s)
# 副本踢出 ISR 条件
# Follower 超过 30s 未同步 → 踢出 ISR
# ISR 的作用
# 1. 保证消息不丢失
# acks=all → 所有 ISR 副本确认后才返回成功
# 2. 保证数据一致性
# Leader 选举时,只从 ISR 中选新 Leader
# 最小 ISR 数
# min.insync.replicas = 2
# 至少 2 个 ISR 副本确认,消息才算成功
# 如果 ISR 数量不足,生产者写入失败
HW 和 LEO
# LEO(Log End Offset):日志末尾偏移量
# 下一条消息将被写入的位置
# HW(High Watermark):高水位
# 消费者只能消费 HW 之前的消息
# HW = min(所有 ISR 副本的 LEO)
# 保证消费者不会读到可能丢失的消息
# 示例
# Leader LEO=100, Follower1 LEO=95, Follower2 LEO=90
# HW = min(100, 95, 90) = 90
# 消费者只能消费 offset < 90 的消息
# HW 的作用
# 1. 保证消费数据一致性
# 2. Leader 切换时,新 Leader 从 HW 开始
# 3. 防止消费者读到未提交的消息
高级拓展
零拷贝(Zero Copy)
# 传统 IO(4 次拷贝,2 次 CPU 拷贝 + 2 次 DMA 拷贝)
# 磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
# 零拷贝(sendfile,2 次拷贝,0 次 CPU 拷贝 + 2 次 DMA 拷贝)
# 磁盘 → 内核缓冲区 → 网卡(DMA 直接传输)
# 数据不经过用户态,CPU 不参与拷贝
# Kafka 使用零拷贝
# 1. 生产者写入:数据写入 Page Cache(内核缓冲区)
# 2. 消费者读取:sendfile 直接从 Page Cache 发送到网卡
# 3. 数据不经过 Kafka 进程的用户空间
# 性能提升
# 传统 IO:CPU 拷贝 2 次,上下文切换 4 次
# 零拷贝:CPU 拷贝 0 次,上下文切换 2 次
# 吞吐量提升 2-3 倍
Controller 选举
# Controller:Kafka 集群的管理者
# 职责:
# 1. 分区 Leader 选举
# 2. ISR 管理
# 3. Broker 上下线处理
# 4. 分区重分配
# Controller 选举(基于 Zookeeper)
# 1. 所有 Broker 竞争创建 /controller 临时节点
# 2. 创建成功的成为 Controller
# 3. Controller 宕机 → 临时节点删除 → 重新选举
# KRaft 模式(Kafka 3.x+,去 Zookeeper)
# 使用 Raft 协议选举 Controller
# 元数据存储在 Kafka 内部 Topic(__cluster_metadata)
实战场景
场景:Kafka 分区数规划
# 分区数规划原则
# 1. 分区数 ≥ 消费者数(一个分区只能被一个消费者消费)
# 2. 分区数不是越多越好
# - 分区越多,文件句柄越多
# - 分区越多,Leader 选举越慢
# - 分区越多,内存占用越大
# 3. 经验值
# - 低吞吐:3-6 个分区
# - 中吞吐:6-12 个分区
# - 高吞吐:12-30 个分区
# 分区数计算公式
# 分区数 = max(目标吞吐量 / 单分区吞吐量, 消费者数)
# 单分区吞吐量:约 10-20 MB/s
# 示例
# 目标吞吐量:100 MB/s
# 单分区吞吐量:10 MB/s
# 消费者数:8
# 分区数 = max(100/10, 8) = 10
面试模拟
面试官:Kafka 为什么这么快?
你:四大原因:顺序写磁盘(日志追加,接近磁盘顺序写极限)、零拷贝(sendfile,数据不经过用户态)、Page Cache(利用操作系统缓存)、批量压缩(减少网络传输)。
面试官:ISR 是什么?有什么作用?
你:ISR 是与 Leader 保持同步的副本集合。作用:保证消息不丢失(acks=all 时所有 ISR 确认)、保证数据一致性(Leader 选举只从 ISR 中选)、通过 HW 保证消费者读到一致的数据。