消息中间件 列表全部文章
消息中间件

Kafka 笔记:分区与副本、消费者组、零拷贝为什么快、以及常用运维命令

Kafka 的核心概念与设计要点:Topic/Partition/Replica/ISR 的关系,消费者组与 offset 提交语义,顺序写 + 零拷贝 + 批量压缩为什么能撑起百万吞吐,附生产配置、常用命令和消息积压、重复消费的处理方式。

更多

Kafka 的定位是分布式流平台,不是传统意义的消息队列。它最擅长的是日志、埋点、行为数据这类「量大、允许少量延迟、要能重放」的场景。

一、核心概念一张图

Topic:order-events(3 分区,2 副本) Partition 0 Leader(Broker 1) Follower(Broker 2) Partition 1 Leader(Broker 2) Follower(Broker 3) Partition 2 Leader(Broker 3) Follower(Broker 1) 消费者组:同组内一个分区只能被一个消费者消费 Consumer A → P0 Consumer B → P1 Consumer C → P2 Consumer D 空闲 分区数决定了组内最大并行度:4 个消费者消费 3 个分区,必有一个空转。分区数一开始就得多配。

关键点:

  • 分区是并行度和顺序性的单位:一个分区内有序,跨分区无序。要保序就把相关消息发到同一分区(指定 key)。
  • 副本与 ISR:每个分区有 Leader 和若干 Follower,ISR(In-Sync Replicas)是与 Leader 保持同步的副本集合。Leader 挂了从 ISR 里选新 Leader。
  • 消费者组:同组内分区独占,不同组各自独立消费(广播效果),offset 按「组 + 分区」维度保存。

二、为什么 Kafka 这么快

四个原因,缺一不可:

  1. 顺序写磁盘:消息追加到分区日志文件末尾。磁盘顺序写的速度接近内存随机写,远超随机写。
  2. 零拷贝:消费时用 sendfile,数据从页缓存直接发到网卡,不经过用户态(见我之前写的 IO 那篇)。
  3. 批量与压缩:Producer 端攒批(linger.ms + batch.size),整批压缩(compression.type=lz4/zstd),网络和存储都省。
  4. 页缓存 + 顺序读:Consumer 读取命中 OS 页缓存,几乎不碰磁盘。

代价也很明确:批量带来延迟。追求极低延迟要调小 linger.ms,但吞吐会下降。这是一对经典的权衡。

三、可靠性的三个配置

# Producer 端
acks=all                       # 等所有 ISR 副本确认(最可靠);1=只等 Leader;0=不等
retries=3                      # 可重试错误自动重试
enable.idempotence=true        # ★ 开启幂等,避免重试产生重复消息
max.in.flight.requests.per.connection=5

# Broker 端
min.insync.replicas=2          # 至少 2 个副本确认才算成功(配合 acks=all)
unclean.leader.election.enable=false   # ★ 不允许非 ISR 副本当 Leader,避免丢消息

# Consumer 端
enable.auto.commit=false       # ★ 关闭自动提交,业务处理完再手动提交

unclean.leader.election.enable=false 是宁可不可用也不丢数据的选择;如果业务允许少量丢失换取可用性,可以设 true。这个开关需要和业务方确认。

四、消费语义怎么选

语义 做法 代价
最多一次 先提交 offset 再处理 可能丢消息
至少一次(默认) 先处理再提交 offset 可能重复消费
精确一次 幂等 Producer + 事务 / 下游幂等 复杂,性能有损

生产上几乎都用「至少一次 + 消费端幂等」:MQ 保证不丢,业务自己保证重复处理不产生副作用。追求「精确一次」的成本远高于在业务侧做幂等。

五、常用命令

# 主题
kafka-topics.sh --bootstrap-server localhost:9092 --list
kafka-topics.sh --bootstrap-server localhost:9092 --create --topic order-events \
  --partitions 12 --replication-factor 3
kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic order-events

# 生产/消费测试
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning

# 消费者组(★ 查积压)
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group order-consumer
# LAG 列就是积压量

# 重置 offset(重放)
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group order-consumer \
  --topic order-events --reset-offsets --to-earliest --execute

# 增加分区(只能加不能减)
kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic order-events --partitions 24

六、积压处理

# 1. 看积压
kafka-consumer-groups.sh --describe --group <group>    # 看 LAG
# 2. 判断是消费慢还是生产涨
#    LOG-END-OFFSET 增速 vs CURRENT-OFFSET 增速
# 3. 扩容消费者到 = 分区数(超过没用)
# 4. 消费逻辑优化:批量化、去掉单条远程调用

扩容上限 = 分区数,所以建 Topic 时分区数要留余量。但分区也不是越多越好:分区多 → 文件多 → 随机写增多 → 实际吞吐下降;还会拉长 Leader 选举时间。一般按「峰值 TPS / 单分区 3000~5000 TPS」估算,再留 2 倍余量。

七、Kafka vs RocketMQ 的实际选择

场景 选谁
日志采集、埋点、用户行为 Kafka
实时计算(Flink 数据源) Kafka
订单、支付、交易链路 RocketMQ(事务消息、延迟消息更成熟)
Topic 数量特别多(上千) RocketMQ(Kafka 分区文件模型在海量 Topic 下吃力)
需要任意时间延迟 都需要自己实现(RocketMQ 5 支持定时消息)

八、几个易错点

  • 分区数只能增不能减,且增加分区会改变 key 到分区的映射,影响顺序性。
  • 消费者数量超过分区数会空转,且触发 Rebalance。
  • Rebalance 期间整个消费组暂停,频繁 Rebalance(如 max.poll.interval.ms 设置过小导致消费者被踢出)是消费停滞的常见原因。
  • auto.offset.reset 只在没有 offset 记录时生效(earliest / latest),已经有 offset 的情况下改它没用,要用 --reset-offsets。
  • 消息默认保留 7 天(retention.ms),超期自动删除,不像 RocketMQ 有「消费完才清」的概念 —— 所以消费者长时间挂掉导致数据过期,是真的会丢。
— 感谢读到这里 —发现错误,或想补充?告诉我 →
TURN KNOWLEDGE INTO SOMETHING REAL

有想法,也想把它做出来?

从技术方案到上线交付,聊聊你的下一个项目。

聊聊需求

一起聊聊

搜索文章

输入关键词,按标题和摘要即时筛选。

浏览分类文章归档

阅读设置

字号
行宽
专注模式

专注模式会隐藏页头与侧栏,只留正文。设置保存在当前浏览器。

稍后阅读

保存在当前浏览器,留给下一次安静阅读。

总浏览 -- 次 · 总访客 -- 人 · 今日浏览 -- 次 · 今日访客 -- 人