Kafka 的定位是分布式流平台,不是传统意义的消息队列。它最擅长的是日志、埋点、行为数据这类「量大、允许少量延迟、要能重放」的场景。
一、核心概念一张图
关键点:
- 分区是并行度和顺序性的单位:一个分区内有序,跨分区无序。要保序就把相关消息发到同一分区(指定 key)。
- 副本与 ISR:每个分区有 Leader 和若干 Follower,ISR(In-Sync Replicas)是与 Leader 保持同步的副本集合。Leader 挂了从 ISR 里选新 Leader。
- 消费者组:同组内分区独占,不同组各自独立消费(广播效果),offset 按「组 + 分区」维度保存。
二、为什么 Kafka 这么快
四个原因,缺一不可:
- 顺序写磁盘:消息追加到分区日志文件末尾。磁盘顺序写的速度接近内存随机写,远超随机写。
- 零拷贝:消费时用
sendfile,数据从页缓存直接发到网卡,不经过用户态(见我之前写的 IO 那篇)。 - 批量与压缩:Producer 端攒批(
linger.ms+batch.size),整批压缩(compression.type=lz4/zstd),网络和存储都省。 - 页缓存 + 顺序读: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 有「消费完才清」的概念 —— 所以消费者长时间挂掉导致数据过期,是真的会丢。
一起聊聊