Kafka 调优与运维实战

Kafka 调优与运维实战

🏠 返回 README | ⬅️ 01-基础与原理.md | ➡️ 03-设计与编码.md

包含章节 :Ch9 调优实战 / Ch10 监控运维 / Ch11 MQ 对比 / Ch12 真实故障案例 难度 :⭐⭐ 高频考点 建议 :本模块是与 简历项目 结合最紧密的部分,建议第 2 周配合自己实际经验复习。Ch12 故障案例按 STAR 模板背 3~5 个,可应付任何"讲讲你解决过的难题"。


📑 本模块目录

  • [9. 生产环境调优实战(高频考点)](#9. 生产环境调优实战(高频考点) "#9-%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E8%B0%83%E4%BC%98%E5%AE%9E%E6%88%98%E9%AB%98%E9%A2%91%E8%80%83%E7%82%B9") ⭐⭐
  • [10. 监控、运维与容量规划](#10. 监控、运维与容量规划 "#10-%E7%9B%91%E6%8E%A7%E8%BF%90%E7%BB%B4%E4%B8%8E%E5%AE%B9%E9%87%8F%E8%A7%84%E5%88%92") ⭐⭐
  • [11. Kafka 与同类 MQ 对比](#11. Kafka 与同类 MQ 对比 "#11-kafka-%E4%B8%8E%E5%90%8C%E7%B1%BB-mq-%E5%AF%B9%E6%AF%94") ⭐
  • [12. 真实故障案例复盘(强烈建议背熟)](#12. 真实故障案例复盘(强烈建议背熟) "#12-%E7%9C%9F%E5%AE%9E%E6%95%85%E9%9A%9C%E6%A1%88%E4%BE%8B%E5%A4%8D%E7%9B%98%E5%BC%BA%E7%83%88%E5%BB%BA%E8%AE%AE%E8%83%8C%E7%86%9F") ⭐⭐⭐

🟡 模块二导引:调优与运维实战

目标 :把"原理"翻译成"参数"和"操作"。能针对一个真实场景(吞吐瓶颈、延迟抖动、ISR 频繁收缩)说出为什么调这个参数、调多少、调完看哪个指标验证

检验标准

  • 能默写 Broker / OS / JVM / Producer / Consumer 5 段式调优清单
  • 能讲清 G1 vs ZGC 在 Kafka 上的取舍
  • 能根据"消息平均大小 + QPS + 副本数 + 保留天数"现场算出磁盘容量与网络带宽
  • 能讲 3 个以上自己经历过的 Kafka 故障(按 STAR 模板)

本模块共 4 章 (Ch9~Ch12)。Ch9 调优Ch12 故障案例是面试性价比最高的两章,建议背熟。


9. 生产环境调优实战(高频考点)

9.1 调优总原则

不要背参数,先讲清目标 → 瓶颈定位 → 调整 → 验证 的方法论。

markdown 复制代码
1. 明确目标:吞吐?延迟?可用性?成本?通常需要权衡,不可能全要。
2. 找瓶颈:是 CPU / 网络 / 磁盘 IO / GC / 锁 / 网络往返?
3. 单变量调整:每次只改一类参数。
4. 压测验证:kafka-producer-perf-test.sh / kafka-consumer-perf-test.sh + 业务压测。
5. 灰度发布:从一个 broker 滚动调,避免一把梭翻车。

9.2 Broker 端调优

properties 复制代码
# 网络与 IO 线程
num.network.threads=6        # 一般 = CPU 核数 / 2
num.io.threads=16            # 一般 = 磁盘数 × 8
queued.max.requests=1000

# Socket buffer
socket.send.buffer.buffer.bytes=1048576
socket.receive.buffer.bytes=1048576
socket.request.max.bytes=104857600   # 100MB,需 ≥ 业务最大消息 × 攒批

# 副本拉取线程(关键!)
num.replica.fetchers=4       # 每个 Broker 的 Fetcher 线程数,3 个 broker 起码 4~8

# 日志保留
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000

# 落盘策略(不要主动 fsync!)
log.flush.interval.messages=Long.MAX_VALUE
log.flush.interval.ms=Long.MAX_VALUE

# 压缩与 cleaner
log.cleaner.threads=2
log.cleaner.dedupe.buffer.size=134217728

# 副本可靠性
default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false
auto.leader.rebalance.enable=true
leader.imbalance.check.interval.seconds=300
leader.imbalance.per.broker.percentage=10

# Topic 默认
num.partitions=12
message.max.bytes=10485760   # 10MB(按业务调整,过大会拖慢一切)

9.3 OS 级调优

bash 复制代码
# 文件描述符
ulimit -n 1000000
echo "* soft nofile 1000000" >> /etc/security/limits.conf
echo "* hard nofile 1000000" >> /etc/security/limits.conf

# 关闭/降低 swap
sysctl -w vm.swappiness=1

# 脏页(控制刷盘抖动)
sysctl -w vm.dirty_background_ratio=5
sysctl -w vm.dirty_ratio=60
sysctl -w vm.dirty_expire_centisecs=12000

# 网络
sysctl -w net.core.somaxconn=32768
sysctl -w net.core.netdev_max_backlog=16384
sysctl -w net.ipv4.tcp_max_syn_backlog=8192

# 磁盘文件系统
# 推荐 XFS > ext4;mount 加 noatime,nodiratime
# 单盘单分区不做 RAID,多盘走 JBOD(让 Kafka 自己平衡)

9.4 JVM 调优(含 G1 vs ZGC vs Shenandoah 选型)

9.4.1 为什么 Kafka Broker 默认推 G1,而不是 ZGC?(高频追问)

这是 10 年开发被狠狠考的题:很多人无脑回答"ZGC 暂停 < 1ms 当然用 ZGC",但用在 Kafka Broker 上几乎没有收益,反而是负作用。要从 Kafka 的内存模型出发讲清楚。

核心结论一句话

Kafka Broker 性能由 PageCache 决定,不由 JVM 堆决定。堆只用 48GB,G1 已经能把 P99 GC 控制在 1020ms 内;ZGC 的"超低延迟"优势体现不出来,反而要付出更高的 CPU、内存放大和读屏障开销。

具体拆解:

1)Kafka Broker 的内存模型决定了堆很小

markdown 复制代码
机器 64GB 内存 → JVM 堆只占 6GB,剩下 ~58GB 全部留给 OS PageCache
                    ↑
            Producer/Consumer/副本同步全部走 PageCache + sendfile
            消息字节流根本不在堆里停留

Broker 堆里到底放了什么?

  • 网络层 buffer(NIO 的 ByteBuffer 大多是 DirectBuffer,走堆外)。
  • ProducerStateManager(PID → 序列号缓存)、Log 元数据、Index 缓存(mmap,走堆外)。
  • Controller 元数据、Coordinator 缓存。
  • 真正落到堆上的对象生命周期都很短,G1 的年轻代复制 + 区域回收就够用了

2)ZGC 的优势场景与 Kafka 不匹配

ZGC 优势场景 Kafka Broker 现状
大堆(几十 GB ~ TB 级) 堆只有 4~8 GB
大对象、大量长寿对象(缓存系统) 大多是短命对象
GC pause 是瓶颈 PageCache miss / 磁盘 IO / 网络才是瓶颈
业务对 P99.9 延迟极度敏感(< 5ms) Kafka 业务普遍能容忍 P99 几十 ms

3)ZGC 在小堆上的反向代价

  • CPU 开销 :ZGC 是并发标记/转移 GC,需要读屏障(Load Barrier) 。每次对象引用读取都要走一次 colored pointer 检查,应用线程吞吐下降 5%~15%。Kafka 是吞吐型系统,这一刀很疼。
  • 内存放大:早期 ZGC 用 colored pointer 后还需要保留多视图(multi-mapping),实际内存占用 > 堆大小。Generational ZGC(JDK 21+)改善了这点,但仍比 G1 多一些 footprint。
  • GC 触发更频繁:堆小 + 吞吐高 → ZGC 并发周期跟不上分配速率,频繁触发并发 GC,CPU 反而打高。
  • 暂停虽短但次数多,对系统总开销不一定更优

4)什么时候 Kafka 才考虑 ZGC?

  • Broker 堆被迫调大(≥ 16GB):例如海量 ProducerStateManager(百万级 PID 的事务场景)、KRaft Controller 节点存超大元数据。
  • JDK 21+ 的 Generational ZGC-XX:+UseZGC -XX:+ZGenerational):分代后年轻代走类似 G1 的快速 minor GC,CPU 开销大幅下降;这种情况下,对延迟敏感的金融场景可以考虑切 ZGC。
  • Kafka Streams / 业务 Consumer (不是 Broker):业务 Consumer 通常堆很大(业务对象、缓存、聚合状态),且对端到端 P99 敏感 → ZGC / Shenandoah 收益明显
9.4.2 G1 推荐配置(Broker,JDK 17+)
bash 复制代码
KAFKA_HEAP_OPTS="-Xms6g -Xmx6g"     # 堆固定大小,避免动态扩缩
KAFKA_JVM_PERFORMANCE_OPTS="
  -server
  -XX:+UseG1GC
  -XX:MaxGCPauseMillis=20            # 目标暂停(G1 会尽量满足,非硬保证)
  -XX:InitiatingHeapOccupancyPercent=35   # 占用率早点触发并发周期
  -XX:G1HeapRegionSize=16m           # 大 region 适配 batch 类大对象
  -XX:G1ReservePercent=20            # 预留区抗 to-space 溢出
  -XX:+ParallelRefProcEnabled        # 引用并行处理
  -XX:+ExplicitGCInvokesConcurrent   # System.gc() 走并发,不全停
  -XX:+AlwaysPreTouch                # 启动期预触摸内存,避免运行期缺页
  -XX:+UseStringDeduplication        # G1 字符串去重,对 Topic/key 多的场景省内存
  -XX:MaxMetaspaceSize=256m
  -XX:+HeapDumpOnOutOfMemoryError
  -XX:HeapDumpPath=/var/log/kafka/
  -Xlog:gc*,gc+heap=trace,safepoint:file=/var/log/kafka/gc.log:time,uptime:filecount=10,filesize=100M
"
9.4.3 Generational ZGC 配置(JDK 21+,仅在确认堆 ≥ 16GB 或对延迟极度敏感时启用)
bash 复制代码
KAFKA_HEAP_OPTS="-Xms16g -Xmx16g"
KAFKA_JVM_PERFORMANCE_OPTS="
  -server
  -XX:+UseZGC
  -XX:+ZGenerational                 # JDK 21 GA
  -XX:+AlwaysPreTouch
  -XX:SoftMaxHeapSize=14g            # 给 ZGC 留缓冲(约 80~85% Xmx)
  -XX:ConcGCThreads=4                # 并发 GC 线程,CPU 充裕可调大
  -XX:ParallelGCThreads=8            # STW 阶段并行线程
  -XX:+HeapDumpOnOutOfMemoryError
  -Xlog:gc*:file=/var/log/kafka/gc.log:time,uptime:filecount=10,filesize=100M
"

生产经验 :我们曾在 KRaft Controller 节点试过 ZGC(堆 24GB,PID 表过亿),P99 GC 暂停从 G1 的 50ms 降到 < 5ms;但普通 Broker 切 ZGC 后 CPU 利用率上升 8%,吞吐下降 6%,最终回滚到 G1。结论:对症下药,不要无脑选最新 GC

9.4.4 GC 排查命令清单
bash 复制代码
# 看 GC 类型与运行参数
jcmd <pid> VM.flags | grep -E "UseG1GC|UseZGC|ConcGCThreads"

# 实时 GC
jstat -gcutil <pid> 1000 30

# GC 日志分析(推荐 GCEasy / GCViewer)
# 关注:YoungGC P99、MixedGC P99、Full GC 次数、Concurrent Cycle Failures

# 看暂停分布
grep "Pause" /var/log/kafka/gc.log | awk '{print $NF}' | sort -n | uniq -c

# 直接抓一次 heap dump(注意全停顿,慎用)
jmap -dump:format=b,file=/tmp/kafka.hprof <pid>
9.4.5 Broker 堆调优红线
  • 不要给 Broker 太大堆(普通 Broker ≤ 8GB)。Kafka 真正的快依赖 PageCache。
  • 机器内存 64GB → 堆 6GB,剩下 58GB 给 PageCache
  • GC 暂停 超过 replica.lag.time.max.ms 的 30%(即 ~9s)就会引发 ISR 抖动。所以 P99 GC 必须远低于这个阈值。
  • 关闭 -XX:+DisableExplicitGC(不能关,Kafka 内部依赖 System.gc() 触发 DirectByteBuffer 回收,否则堆外内存泄漏直至 OOM)。这是个经典坑。
  • 永远启用 +AlwaysPreTouch,避免上线后第一波流量打缺页风暴。

9.5 Producer 端调优

properties 复制代码
# 高吞吐场景
acks=all
enable.idempotence=true
batch.size=131072            # 128KB
linger.ms=20
compression.type=lz4         # 或 zstd(更高压缩比,CPU 略高)
buffer.memory=134217728      # 128MB
max.in.flight.requests.per.connection=5
delivery.timeout.ms=120000
request.timeout.ms=30000

调优心法:

  • 看 Producer JMX:record-queue-time-avg(等批耗时)↑ → linger.ms 大了或 batch 大了;record-send-rate 上不去 → 看是否 buffer.memory 满。
  • 业务侧不要在 callback 里做重活(Sender 单线程会被拖慢)。

9.6 Consumer 端调优

properties 复制代码
fetch.min.bytes=65536
fetch.max.wait.ms=100
max.partition.fetch.bytes=1048576
max.poll.records=200
max.poll.interval.ms=300000
session.timeout.ms=45000
heartbeat.interval.ms=15000
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor
isolation.level=read_committed     # 如果生产端用事务

实战套路:

  • 业务慢 → 不要无脑放大 max.poll.interval.ms,先做线程池异步化
  • 监控 records-lag-max(落后条数)+ fetch-latency-avg,定位是消费慢还是拉得慢。

9.7 真实面试现场题(5 道带公司风格标记)


🟦 字节风格 Q1:你给生产 Broker 配 Xmx=8g,CPU 64 核,内存 256GB。线上 P99 写入毛刺到 200ms。怎么定位?

字节"性能压榨"经典题。考点:能不能从 GC / PageCache / 磁盘 / 锁四个维度系统排查。

参考答案:四步定位法。

  1. 看 GCjstat -gcutil <pid> 1000 看 G1 暂停。如果 P99 GC > 50ms:
    • 检查 G1 region 大小,调到 16M
    • 检查 InitiatingHeapOccupancyPercent,从默认 45% 降到 35%
    • 关键:检查 Heap 是不是太大了,> 8GB 时 G1 暂停容易飙升
  2. 看 PageCachefree -h 看 buff/cache 是否被吃掉。vmstat 1bi/bo
    • 如果有大量 swap → swappiness 调到 1
    • 如果 PageCache miss 率高 → 检查是否 Topic 太多导致命中率下降
  3. 看磁盘iostat -x 1await/util
    • util > 80% → 磁盘瓶颈,检查是不是 compact / log clean 同步触发
    • vm.dirty_background_ratio 从 10 → 5,让脏页提前后台刷
  4. 看锁 :JMX RequestQueueTimeMs P99 是否飙:
    • 高 → Handler 线程不够,调大 num.io.threads
    • 低 → 锁竞争(async-profiler -e lock 抓一下)

追问:你怎么区分是"GC 抖" vs "Page Cache miss" vs "磁盘抖"导致的写入毛刺?

  • 三者都可能让 LogFlushRateAndTimeMs P99 飙高,但RequestQueueTimeMs 同步飙高 = GC/锁;只 LogFlush 飙 = 磁盘;BytesIn 不变 + 命中率掉 = PageCache

🟧 阿里风格 Q2:你们集群从 ZK 模式升级到 KRaft,怎么做?停机窗口给你 4 小时。

阿里"稳定性 + 平台演进"经典题。考点:升级路径 + 风险预案 + 监控验证。

参考答案

markdown 复制代码
阶段 1:评估(提前 2 周)
  - 当前版本 ≥ 3.4?不到先升级到 3.4+
  - 是否有元数据规模超出 KRaft 默认 quorum 容量
  - 备份所有 Topic / ACL / Quota 配置

阶段 2:迁移(当天 4h 窗口)
  - 步骤 1(30min):启动 3 个独立的 KRaft Controller
  - 步骤 2(1h):执行 ZK → KRaft 元数据迁移工具
  - 步骤 3(30min):各 Broker 重启切换为 KRaft 模式
  - 步骤 4(30min):验证元数据正确性
  - 步骤 5(1h):滚动把流量切回(先小流量、后大流量)

阶段 3:监控
  - ActiveControllerCount = 1
  - UnderReplicatedPartitions = 0
  - Producer/Consumer 客户端无异常报错
  - Controller 故障切换毫秒级

回滚预案:
  - 任一步骤失败 → 全量回滚到 ZK 模式(保留 ZK 元数据快照)
  - 时间窗口预留 1h buffer

追问:生产风险点是什么?

  • KRaft 模式下 Controller 故障切换更快但对 Quorum 网络更敏感,跨机房 RTT > 50ms 慎用。
  • KRaft 早期版本(3.4/3.5)有过 metadata snapshot 大文件的 bug,3.6+ 才稳。
  • 更推荐:新建 KRaft 集群 + MM2 数据迁移 + 业务侧切流量,零停机但成本高。

🟪 蚂蚁风格 Q3:金融场景,你怎么权衡 acks=all + min.insync.replicas=2 带来的可用性下降?

蚂蚁"金融可用性 vs 一致性"经典题

参考答案

ini 复制代码
风险:
  3 副本 + ISR=2 → 任一 broker 故障仍可写
  但若 2 个 broker 同时故障(机架掉电、跨机架)→ 拒绝写入
  → 业务侧 Producer 阻塞 / 失败

应对(多层兜底):
  1) 副本放置:Rack-Aware(broker.rack=zone-a/b/c),3 副本跨 3 机架
  2) 故障演练:定期 chaos test 拉掉 1 个 broker,验证 ISR=2 仍可写
  3) Producer 端降级:buffer.memory 满 + 短期失败 → 写本地 disk-based fallback
  4) 业务侧补偿:依靠 Outbox 表 + Debezium 兜底(DB 一旦提交,最终消息一定送达)
  5) 告警分级:UnderMinIsr → P0;UnderReplicated → P1
  6) 降级开关:极端情况下临时把 min.insync.replicas 改 1(牺牲一致性保可用性)
     → 配合资损监控 + 事后对账修复

追问:万一真的丢消息了,怎么发现?

  • 不能只靠 Lag 监控(先消费后处理时 Lag=0 但消息已丢)
  • 端到端时延埋点:消息生产时间戳 vs 业务落地时间
  • 对账系统:DB 流水 vs Kafka 消息条数日终对账
  • 业务关键链路:每条消息持久化到 Outbox + Audit log 双写

🟢 腾讯风格 Q4:日均 10 万亿消息的集群,你怎么把单 GB 消息成本从 0.5 元降到 0.3 元?

腾讯"海量 + 成本"经典题。考点:能从存储/网络/CPU 三个维度找出真正的成本大头。

参考答案

markdown 复制代码
1) 压缩升级(主战场,省最多)
   - lz4 → zstd(压缩比 0.4 → 0.3)
   - 预计节省存储 25% + 网络 25%
   - CPU 上升 ~10%,可接受

2) Tiered Storage(KIP-405)
   - 热数据 1 天本地 NVMe,> 1 天下沉 S3
   - S3 单 GB 成本是本地 SSD 的 1/10
   - 占总数据量 90%+ 的冷数据走 S3 → 整体磁盘成本降 60%

3) 副本数差异化
   - 核心 Topic:副本 3
   - 日志类 Topic:副本 2(容忍丢万分之一,业务可重试)
   - 整体磁盘节省 17%

4) Quota 限流 + 业务收敛
   - 监控异常突增的 Topic(流量翻倍)
   - 强制 Topic owner 优化(拉黑必要时)
   - 历史包袱清理(无人订阅的 Topic 直接 archive)

5) 跨 AZ 流量优化
   - Follower Fetch(KIP-392)让 Consumer 就近读
   - 跨 AZ 流量降 50%

合计:单 GB 成本从 0.5 → 0.3,年省约 X 千万。

追问:zstd 压缩 CPU 涨多少?业务能接受吗?

  • 实测:zstd level 3,CPU +10%;level 6,CPU +25% 但比例再降 5%。
  • 业务侧权衡:CPU 成本 vs 存储/带宽成本。在腾讯这种"带宽 = 磁盘 3 倍"的体系下,zstd 一定划算。

🟡 美团/快手风格 Q5:实时推荐系统的 Kafka 集群 P99 延迟从 20ms 飙到 200ms,怎么排查?

美团/快手"实时性"经典题。考点:从客户端到 broker 全链路定位。

参考答案

markdown 复制代码
全链路打点排查:
1) Producer 端
   - record-queue-time-avg:消息在 RecordAccumulator 等多久?
   - request-latency-avg:请求发到 broker 多久?
   → 任一飙升 → 网络问题 / linger.ms 设置不合理

2) Broker 端
   - RequestQueueTimeMs:进队列等待
   - LocalTimeMs:本地写盘
   - RemoteTimeMs:等 ISR 同步(acks=all 才有)
   - ResponseQueueTimeMs:回包等待
   → 看哪段最长

3) Consumer 端
   - fetch-latency-avg:fetch 请求 RTT
   - records-consumed-rate:消费速率
   - records-lag-max:消费 lag

定位场景:
  - LocalTimeMs 飙 → 磁盘 / GC 抖
  - RemoteTimeMs 飙 → 某个 ISR 副本掉队(GC / 网络)
  - RequestQueueTimeMs 飙 → Handler 线程不够
  - Producer record-queue-time 飙 → 客户端攒批太久(linger.ms)

修复(按场景):
  - 副本掉队 → 该 broker GC 调优 / 磁盘升级
  - Handler 不够 → num.io.threads 8 → 16
  - 客户端攒批 → 实时场景 linger.ms 0~5(牺牲吞吐换延迟)

追问:实时推荐场景,linger.ms=0 vs linger.ms=5,你选哪个?

  • 取决于业务时延要求。如果 P99 < 50ms 必须命中,linger=0;如果 P99 < 200ms 可接受,linger=5 提升 5~10% 吞吐。
  • 真正的优化是业务侧异步发送 + 框架层 fire-and-forget + 服务端 ack=1

10. 监控、运维与容量规划

本章定位 :从"装上 Kafka 能跑"到"出问题前 5 分钟报警、出问题后 30 分钟止血"。 核心思想监控不是装一堆指标,是建立"故障 → 指标 → 处置预案"的链路

10.1 监控体系总览

flowchart LR classDef src fill:#dbeafe,stroke:#1d4ed8 classDef collect fill:#fef9c3,stroke:#a16207 classDef store fill:#fee2e2,stroke:#b91c1c classDef vis fill:#dcfce7,stroke:#15803d classDef alert fill:#f3e8ff,stroke:#7e22ce K[Kafka Broker JMX]:::src P[Producer JMX]:::src C[Consumer JMX]:::src OS[OS / Disk / NIC]:::src K --> JE[JMX Exporter
jmx_prometheus_javaagent]:::collect P --> JE2[Micrometer / 自研埋点]:::collect C --> JE2 OS --> NE[Node Exporter]:::collect JE --> Prom[(Prometheus)]:::store JE2 --> Prom NE --> Prom Prom --> Graf[Grafana 大盘]:::vis Prom --> AM[AlertManager]:::alert AM --> PD[PagerDuty / 钉钉 / 飞书]:::alert Burrow[Burrow]:::collect --> CG[__consumer_offsets 监控] CC[Cruise Control]:::collect --> Bal[自动均衡 + 异常告警]

关键组件分工

组件 作用 必要性
JMX Exporter 把 Kafka JMX MBean 暴露为 Prometheus metrics 必备
Node Exporter 主机层指标(CPU / Disk / NIC) 必备
Prometheus 时序数据库 + 告警规则引擎 必备
Grafana 可视化大盘 必备
AlertManager 告警路由、抑制、分级 必备
Burrow LinkedIn 出品的 Consumer Lag 监控(基于 offset 滑动窗口) 推荐
Cruise Control LinkedIn 出品的自动副本均衡 + 异常检测 推荐
Kafka UI / Kafdrop / AKHQ Topic / Group 可视化管理 推荐

10.2 必看 JMX 指标(按层级 + 严重等级)

10.2.1 集群健康(P0 红线指标,触发立即告警)
指标 含义 告警阈值 含义解读
kafka.controller:type=KafkaController,name=OfflinePartitionsCount 离线分区数 > 0 立即告警 有 partition 没有 Leader → 直接不可用
kafka.controller:type=KafkaController,name=ActiveControllerCount 当前 Controller 数量 不等于 1 告警 0 = 集群无主;> 1 = 脑裂
kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions 副本未同步分区数 > 0 持续 1 分钟 有副本掉队,可能丢数据风险
kafka.server:type=ReplicaManager,name=UnderMinIsrPartitionCount ISR 不足 min.insync.replicas 的分区数 > 0 立即告警 Producer acks=all 写入会失败
kafka.server:type=ReplicaManager,name=AtMinIsrPartitionCount ISR 刚好等于 min.insync 的分区数 持续增长 告警 距离 ISR 不足只差一台
10.2.2 性能与吞吐(P1 容量指标)
指标 含义 关注点
kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec 入流量 byte/s 容量评估 + 限流
kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec 出流量(含副本同步) 网络带宽规划
kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec 入消息条数 QPS 业务量监控
kafka.server:type=BrokerTopicMetrics,name=BytesRejectedPerSec 因配额/大小被拒绝的字节数 > 0 必看,业务被限流
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce Produce 端到端耗时(含 Local + Remote + Response Queue) P99 > 200ms 告警
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchConsumer Consumer Fetch 耗时 P99 > 100ms
kafka.network:type=RequestMetrics,name=TotalTimeMs,request=FetchFollower Follower 同步耗时 飙升 → ISR 风险
kafka.network:type=RequestMetrics,name=RequestQueueTimeMs 请求在队列里等待时间 飙升 = Handler 线程不够 / 锁争用
kafka.network:type=RequestMetrics,name=ResponseQueueTimeMs 响应队列等待时间 飙升 = Processor 不够
10.2.3 资源使用(P2 资源指标)
指标 含义 阈值
kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent Network 线程空闲率 < 30% 告警(线程不够)
kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent Handler 线程空闲率 < 30% 告警
kafka.log:type=LogFlushStats,name=LogFlushRateAndTimeMs 刷盘耗时 P99 飙升 → 怀疑磁盘
kafka.server:type=ReplicaFetcherManager,name=MaxLag Follower 落后 Leader 最大 offset 持续 > 1000 告警
kafka.server:type=DelayedOperationPurgatory,name=PurgatorySize,delayedOperation=Produce DelayedProduce 等待数量 飙升 = HW 推不动(Follower 卡)
10.2.4 客户端侧(Producer / Consumer)

Producer

指标 含义
record-send-rate 发送速率
record-error-rate 发送失败率
record-retry-rate 重试率(飙高说明 broker 在抖动
request-latency-avg / max 单次 Produce 请求延迟
record-queue-time-avg 消息在 RecordAccumulator 里等待时间
buffer-available-bytes 缓冲池剩余(接近 0 = 即将阻塞 send)

Consumer

指标 含义
records-lag-max 最大滞后条数(核心指标
records-consumed-rate 消费速率
fetch-latency-avg Fetch 平均延迟
commit-latency-avg offset 提交延迟
rebalance-rate-per-hour rebalance 频率(> 1 次/小时 警告
last-rebalance-seconds-ago 距上次 rebalance 时长
10.2.5 Lag 监控的正确姿势

⚠️ Burrow 的 Lag = 0 不代表业务零延迟(参考 Ch12 案例 5)。

正确的监控分层:

markdown 复制代码
1. offset Lag    →  Burrow / Kafka Exporter
2. 端到端时延    →  消息时间戳 vs 业务落地时间(业务侧自埋点)
3. 业务正确性    →  对账 / 抽样回放

10.3 关键告警规则示例(Prometheus)

yaml 复制代码
groups:
  - name: kafka-critical
    rules:
      - alert: KafkaOfflinePartitions
        expr: kafka_controller_kafkacontroller_offlinepartitionscount > 0
        for: 1m
        labels:
          severity: P0
        annotations:
          summary: "Kafka 集群有离线分区 ({{ $value }} 个)"
          runbook: "https://wiki/runbook/kafka-offline-partition"

      - alert: KafkaUnderReplicated
        expr: kafka_server_replicamanager_underreplicatedpartitions > 0
        for: 5m
        labels:
          severity: P1
        annotations:
          summary: "Kafka URP 持续 5 分钟 ({{ $value }})"

      - alert: KafkaUnderMinIsr
        expr: kafka_server_replicamanager_underminisrpartitioncount > 0
        for: 1m
        labels:
          severity: P0
        annotations:
          summary: "Kafka 有分区 ISR 不足 min.insync.replicas,影响 Producer 写入"

      - alert: KafkaProduceLatencyHigh
        expr: histogram_quantile(0.99, rate(kafka_network_requestmetrics_totaltimems_request_produce_bucket[5m])) > 200
        for: 10m
        labels:
          severity: P2
        annotations:
          summary: "Kafka Produce P99 > 200ms 持续 10 分钟"

      - alert: KafkaConsumerLagHigh
        expr: kafka_consumergroup_lag{consumergroup="order-service"} > 100000
        for: 5m
        labels:
          severity: P1
        annotations:
          summary: "Consumer {{ $labels.consumergroup }} Lag={{ $value }}"

      - alert: KafkaIsrShrinkRate
        expr: rate(kafka_server_replicamanager_isrshrinkspersec[5m]) > 0.5
        for: 5m
        labels:
          severity: P2
        annotations:
          summary: "ISR 收缩频繁,疑似 GC / 网络 / 磁盘抖动"

10.4 容量规划详细方法

10.4.1 计算公式
scss 复制代码
[输入参数]
单条消息平均大小   M (Byte)
峰值 QPS          Q  
副本数            R  (一般 3)
保留时长          T  (天)
预留余量          F  (1.3 ~ 1.5)
压缩比            C  (lz4 ≈ 0.4,zstd ≈ 0.3)
Consumer 组数     G

[输出]
磁盘总需求    = M × Q × 86400 × T × R × F × C    (压缩后实际占用)
磁盘单 broker = 总需求 / broker 数
入网带宽      = M × Q
出网带宽      = M × Q × (G + R - 1)   ← R-1 是副本同步消耗
内存最少      = max(6GB 堆, 30% 机器内存留 PageCache)
CPU           = QPS / 单核处理量(lz4 压缩约 2 万条/核/秒)
10.4.2 实战示例:日均 100 亿消息系统
ini 复制代码
输入:
  日消息总量 = 100 亿
  平均 QPS = 100亿 / 86400 ≈ 12 万
  峰值 QPS(按 5 倍)= 60 万
  M = 1 KB
  T = 7 天
  R = 3
  F = 1.5
  C = 0.4 (lz4)
  G = 5 (5 个消费组)

输出:
  磁盘 = 1024 × 60万 × 86400 × 7 × 3 × 1.5 × 0.4 ≈ 670 TB
  → 每 broker 24×7TB NVMe → 净 168 TB → 至少 5 台
  → 实际加 50% 余量 → 8 台

  入网 = 1024 × 60万 = 600 MB/s ≈ 4.8 Gbps
  出网 = 1024 × 60万 × (5 + 2) = 4.2 GB/s ≈ 33.6 Gbps
  → 25 Gbps 网卡 × 2 (bond)

  内存 = 每 broker 256GB(堆 8GB + PageCache 248GB)

  CPU = 60万 / 2万 = 30 核 (单 broker,含 SSL/压缩)
  → 64 核
10.4.3 容量水位预警
erlang 复制代码
磁盘水位     单 broker 使用率 < 70% (留 30% 给 segment roll + compact)
网卡水位     使用率 < 60% (留 40% 应对突发)
PageCache 命中率 ≥ 95%(用 vmstat 看 cache miss)
GC 时间占比  < 1%

10.5 集群扩容流程(实战)

10.5.1 扩容步骤
sequenceDiagram participant 运维 participant 新Broker participant Controller participant 老Broker participant 监控 运维->>新Broker: 1) 部署 Kafka,配置相同 cluster.id 新Broker->>Controller: 2) 注册到集群 Controller->>监控: 3) Broker 数 +1 运维->>运维: 4) 生成 reassignment.json (kafka-reassign-partitions --generate) 运维->>Controller: 5) --execute 提交重分配(带 throttle 限速!) Controller->>老Broker: 6) 老 broker 发送数据给新 broker (Follower Fetch) Controller->>新Broker: 7) 新 broker 写入并加入 ISR 监控->>运维: 8) 实时监控 UnderReplicated + 网络 运维->>Controller: 9) --verify 验证完成 运维->>Controller: 10) 移除 throttle (--remove-throttle)
10.5.2 关键命令
bash 复制代码
# 1. 列出待迁移 topic
echo '{"topics":[{"topic":"order-event"}],"version":1}' > topics-to-move.json

# 2. 生成迁移计划(指定目标 broker 列表)
kafka-reassign-partitions.sh --bootstrap-server $BS \
    --topics-to-move-json-file topics-to-move.json \
    --broker-list "1,2,3,4,5,6" \
    --generate > plan.json

# 3. 提取 Proposed partition reassignment 部分到 reassign.json

# 4. 执行(带限速!默认无限速会打挂集群)
kafka-reassign-partitions.sh --bootstrap-server $BS \
    --reassignment-json-file reassign.json \
    --execute \
    --throttle 50000000     # 50 MB/s 单 broker 限速

# 5. 验证
kafka-reassign-partitions.sh --bootstrap-server $BS \
    --reassignment-json-file reassign.json \
    --verify

# 6. 移除限速(一定要做,否则会一直限制)
kafka-configs.sh --bootstrap-server $BS --alter \
    --entity-type brokers --entity-name 1 \
    --delete-config follower.replication.throttled.rate,leader.replication.throttled.rate
10.5.3 扩容期监控重点
markdown 复制代码
1. UnderReplicatedPartitions    → 必然短暂飙升,不要紧张
2. 网卡使用率                   → 限速时应该贴近 throttle 上限
3. Producer P99                 → 受影响应 < 30%,否则 throttle 调小
4. Consumer Lag                 → 不应有显著增长

10.6 缩容 / 下线流程

bash 复制代码
# 1. 把目标 broker 的所有 partition 迁出去
echo '{"topics":[{"topic":"*"}],"version":1}' > all-topics.json
kafka-reassign-partitions.sh ... --topics-to-move-json-file all-topics.json \
    --broker-list "1,2,3,4,5"   # 不包含要下线的 broker 6

# 2. 等迁移完成
kafka-reassign-partitions.sh ... --verify

# 3. 确认目标 broker 上没有 partition
kafka-broker-api-versions.sh --bootstrap-server <broker6> ...

# 4. 优雅停机(kafka-server-stop.sh,发 SIGTERM)
# 5. 等待 controlled.shutdown 完成(看 server.log "Controlled shutdown completed")

⚠️ 不能直接 kill -9,会让所有 partition Leader 在该 broker 上的瞬间不可用。

10.7 滚动升级流程

flowchart TB A[1. 准备:备份配置 + 充分压测] --> B[2. inter.broker.protocol.version 设为旧版本] B --> C[3. 一台一台滚动升级 Broker] C --> D[4. 每台升级后等 ISR 完整 + URP=0] D --> E{所有 broker 升级完?} E -->|否| C E -->|是| F[5. 升级 inter.broker.protocol.version 到新版本] F --> G[6. 升级 log.message.format.version] G --> H[7. 升级 Client 端]

关键参数

properties 复制代码
# 升级期间临时设置:让新 broker 用旧协议与老 broker 通信
inter.broker.protocol.version=2.8
log.message.format.version=2.8

# 全部升级完后再统一切到 3.7
inter.broker.protocol.version=3.7
log.message.format.version=3.7

避坑

  • 2.8 → 3.x 跨大版本:先从 2.8 升到 3.3,再升到目标版本(不能直接跨太多版本)。
  • ZK → KRaft :3.4+ 提供 migration 工具(kafka.tools.MetadataQuorumCommand),但生产慎用,建议新建 KRaft 集群 + MM2 迁移数据更安全。

10.8 Cruise Control 自动均衡

Cruise Control 是 LinkedIn 开源的 Kafka 自动运维利器。

能力

  • 检测异常:goal violation(如 broker CPU 不均衡、磁盘倾斜)。
  • 自动均衡:生成 + 执行 reassignment 计划,带限速。
  • 故障自愈:broker 挂了自动迁移 partition。
  • 监控指标:Anomaly Detector + REST API。

部署架构

arduino 复制代码
Cruise Control Server
   ├── Metric Sampler(采集 broker JMX)
   ├── Goal Optimizer(多目标优化算法)
   └── Executor(执行 reassignment)
        ↓
     Kafka Cluster

典型 goal

  • RackAwareGoal - 副本跨机架分布
  • ReplicaCapacityGoal - 单 broker 副本数上限
  • DiskCapacityGoal - 磁盘水位
  • NetworkInboundCapacityGoal - 入网带宽
  • LeaderReplicaDistributionGoal - Leader 数量均衡

实战经验

  • 不要把所有 goal 都打开,按业务优先级选 5~7 个;
  • 限速默认很激进(10MB/s),生产建议提到 50~100MB/s;
  • 先用 dry-run 模式跑一周观察推荐再开自动执行;
  • 监控 Cruise Control 自身的 anomalies 接口。

10.9 备份与灾备

10.9.1 数据备份方案
方案 描述 适用
多副本 3 副本是基础保障 必备,应对单点故障
MirrorMaker 2 跨集群异步复制 灾备机房
Tiered Storage(KIP-405) 冷数据下沉 S3 长期归档
数据湖双写 同时写 Kafka + Iceberg/Hudi 数据可回放
10.9.2 配置与元数据备份
bash 复制代码
# 备份所有 topic 配置
kafka-topics.sh --bootstrap-server $BS --describe > topics-backup.txt

# 备份所有 ACL
kafka-acls.sh --bootstrap-server $BS --list > acls-backup.txt

# 备份所有 quota
kafka-configs.sh --bootstrap-server $BS --describe \
    --entity-type users --entity-default > quotas-backup.txt

# KRaft 模式:snapshot 自动持久化在 metadata log,无需手动备份

10.10 高频追问

Q:UnderReplicatedPartitions = 0,但 ISR 持续抖动怎么排查?

A:看 IsrShrinksPerSecIsrExpandsPerSec

  1. 同时升高 → 副本反复进出 ISR(replica.lag.time.max.ms 设小了 / GC 抖 / 网络丢包)。
  2. 只升 Shrink → Follower 落后越来越多(broker 负载过高)。
  3. 工具:kafka-leader-election.sh 临时强制 prefer leader 选举,结合 kafka-log-dirs.sh 看磁盘倾斜。

Q:发现某 partition Lag 不下降,offset 也不前进?

A:可能场景:

  1. Consumer 进程挂了但 Group 没及时变化 → kafka-consumer-groups.sh --describe 看 LAG / CONSUMER-ID 是否为 -
  2. Consumer 卡在某条消息 → 看消费者 thread dump,常见 DB / Redis 阻塞。
  3. Poison message:单条消息反复抛异常 + 自动重试无限循环 → 写入 DLQ + skip。

Q:滚动升级时,业务侧 Producer/Consumer 会断连吗?

A:理论上不会。客户端有 metadata refresh + retry 机制,broker 短暂下线时客户端会自动重连其他 broker。但实际有几个坑:

  1. request.timeout.ms 设太短(< 30s)→ 升级期间个别请求超时。
  2. delivery.timeout.ms 设太短(< 60s)→ batch 直接 expire 丢消息。
  3. 老版本(< 2.8)滚动升级时 controller 切换可能有几秒抖动。 最佳实践 :升级前一周临时把 delivery.timeout.ms 提到 5 分钟。

Q:你们 Kafka 集群 SLA 怎么定的?

A:参考标准:

  • 可用性:99.95% / 99.99%(金融)
  • 数据持久性:99.9999%(六个 9)
  • 写入 P99 延迟:< 100ms
  • Consumer Lag:< 10s(核心 Topic)/ < 5min(离线日志)
  • 故障恢复 RTO :< 5min;RPO:< 1min

11. Kafka 与同类 MQ 对比

本章定位 :选型题、设计题最常被追问。要能讲清楚"为什么这个场景选 Kafka 而不是 RocketMQ"。

11.1 全景对比

维度 Kafka RocketMQ RabbitMQ Pulsar
核心模型 分区日志(Partitioned Log) 主题 + 队列 + CommitLog Exchange / Queue / Binding 分层(Broker + BookKeeper)
存储 多文件(每分区一目录) 单 CommitLog + 多 ConsumeQueue 索引 内存 + 磁盘混合 BookKeeper Ledger
吞吐 极高(百万级 QPS) 高(10w~50w QPS) 中(万级) 高(百万级)
延迟 ms 级 ms 级 μs 级(小消息) ms 级
顺序 分区内有序(hash key) 队列内有序(messageQueueSelector) 单队列有序(FIFO) 分区内 / Key 有序
事务 2PC + Control Records 半消息 + 回查 弱(Channel 层 commit) 2PC
延迟消息 ❌ 不原生(要业务自实现) ✅ 18 个固定级 / 5.0 任意精度 ✅ 插件 / TTL+DLX ✅ 原生秒级
死信队列 ❌ 业务自实现 ✅ 原生(%DLQ%xxx) ✅ 原生 DLX ✅ 原生
消息回溯 ✅ 强(offset reset) ✅ 支持(按时间) ❌ 弱(消息消费即删) ✅ 强
重试机制 ❌ 业务自实现 ✅ 16 级阶梯重试 一般 一般
广播 / 集群消费 集群(Group) 集群 + 广播双模式 Topic / Direct / Fanout Exclusive/Shared/Failover/KeyShared
多租户 弱(仅 Quota) 一般 强(Tenant/Namespace/Topic 三层)
存算分离 否(Tiered Storage 分层但不分离)
协议 自研二进制 自研(兼容 Java/Push/Pull) AMQP 0.9.1 / STOMP / MQTT 自研 + 兼容 Kafka
生态 极强(Connect、Streams、Schema Registry、Flink、Spark、Iceberg) 阿里系 + 业务消息生态 中(运维成熟) 一般(成长中)
运维复杂度 高(Broker + BookKeeper + ZK)
典型用法 日志、大数据、流处理 订单、交易、业务消息 低吞吐高可靠业务 多租户、跨地域 SaaS

11.2 Kafka vs RocketMQ(最高频对比)

11.2.1 存储模型对比(关键差异)
flowchart TB subgraph Kafka_Storage["Kafka 存储模型:每 Partition 独立文件"] direction TB K1["Topic A - Partition 0
00000000.log
00000000.index"] -.-> K2[磁盘文件] K3["Topic A - Partition 1
00000000.log
00000000.index"] -.-> K2 K4["Topic B - Partition 0
00000000.log
00000000.index"] -.-> K2 end subgraph RocketMQ_Storage["RocketMQ 存储模型:单 CommitLog + 索引"] direction TB R1[Producer 1 写入 Topic A] --> RC[CommitLog
所有消息混合顺序写] R2[Producer 2 写入 Topic B] --> RC R3[Producer 3 写入 Topic C] --> RC RC --> RQ1["ConsumeQueue
(Topic A, Q0) → offset"] RC --> RQ2["ConsumeQueue
(Topic A, Q1) → offset"] RC --> RQ3["ConsumeQueue
(Topic B, Q0) → offset"] end
特性 Kafka RocketMQ
写入磁盘模式 每分区独立文件,多文件并行顺序写 单 CommitLog 全局顺序写
Topic 数量上限 受单 broker 文件句柄限制(数千 Partition) 支持数万 Topic(CommitLog 共享)
单分区读取 直接读分区文件,sendfile 零拷贝 通过 ConsumeQueue 索引到 CommitLog 偏移,多一次随机读
顺序写优势 多文件聚合可能成随机写(高 Topic 数时) 一个文件,永远顺序写

结论

  • 少 Topic 高吞吐(< 1000 Topic):Kafka 更快。
  • 多 Topic(万级)业务消息:RocketMQ 单 CommitLog 设计更优。
11.2.2 顺序消息对比
维度 Kafka RocketMQ
API 易用性 用 key 哈希到分区,需理解 Partitioner MessageQueueSelector 显式指定 Queue
严格顺序 单分区单 Consumer,max.in.flight=1(或幂等≤5) 单 Queue 顺序消费,自动加锁保证
顺序消费失败 业务自处理(重试/暂停) 自动暂停 + 重试 16 次 + 进 DLQ
顺序消费的 Rebalance 影响 重新分配后短暂乱序 锁机制保证不乱序

顺序场景下 RocketMQ 更省心,Kafka 需要业务方自实现严格顺序保证。

11.2.3 事务消息对比
维度 Kafka RocketMQ
协议 真正的 2PC(Coordinator + Markers) 半消息 + 反查(单阶段 + 异步检查
业务侵入 transactional.id 包装代码 实现 TransactionListener 接口
超时回查 TC 超时 abort 业务回调 checkLocalTransaction
跨集群事务 ❌ 不支持 ❌ 不支持
典型场景 Flink Sink 端到端 EOS 订单创建 + 业务表写入原子性

RocketMQ 半消息流程面试要会画):

sql 复制代码
1. Producer 发送 半消息(Half Message)→ Broker 存储但不投递
2. Producer 执行本地事务(DB update)
3. Producer 提交 Commit / Rollback
   ├─ Commit  → Broker 让消息可见
   └─ Rollback → Broker 删除半消息
4. 如果 Producer 没确认 → Broker 主动回查 Producer 的状态
   → checkLocalTransaction() → COMMIT / ROLLBACK / UNKNOWN
11.2.4 延迟消息对比
维度 Kafka RocketMQ
原生支持
精度 业务自实现,分桶或时间轮 4.x:18 个固定级(1s/5s/10s/...);5.0+:任意精度
实现 多 Topic 分桶 / 自研时间轮服务 内置时间轮 + 18 个 SCHEDULE_TOPIC_xxxx
业务复杂度

结论:业务延迟消息一定选 RocketMQ。

11.2.5 选型决策矩阵

11.3 Kafka vs RabbitMQ

11.3.1 模型差异
flowchart LR subgraph RabbitMQ P1[Producer] --> Ex[Exchange
direct/topic/fanout/headers] Ex --> B1{Binding
routing key 路由} B1 --> Q1[Queue 1] B1 --> Q2[Queue 2] B1 --> Q3[Queue 3] Q1 --> C1[Consumer] Q2 --> C2[Consumer] Q3 --> C3[Consumer] end subgraph Kafka P2[Producer] -->|key hash| T[Topic Partitioned] T --> P0[Partition 0] T --> Pa1[Partition 1] T --> Pa2[Partition 2] P0 --> CG[Consumer Group
每 partition 一个 consumer] Pa1 --> CG Pa2 --> CG end
维度 RabbitMQ Kafka
路由模型 Exchange + Binding 灵活路由 Partition 哈希(key)
消息删除 消费成功即删 按时间 / 大小保留
消息回溯 ❌(消费即删) ✅ 支持
吞吐量 万级 百万级
延迟 μs 级(小消息) ms 级
协议 AMQP / STOMP / MQTT 自研二进制
典型场景 银行间、低吞吐金融、复杂路由 大数据、日志、流处理
11.3.2 何时还会选 RabbitMQ?
  1. 复杂路由:headers exchange、按多 header 字段灵活分发;
  2. 低吞吐高一致性:传统业务异步、< 万级 QPS;
  3. 协议要求:与 STOMP / MQTT 互通;
  4. 小消息低延迟:μs 级延迟(金融报价转发)。

11.4 Kafka vs Pulsar

详细架构对比见 Ch17.6,这里给精简版选型。

何时选 Pulsar 何时坚守 Kafka
需要超大 Topic 数(10w~100w) 大数据生态主导(Flink/Spark/Iceberg)
多租户 SaaS(强隔离) 团队对 Kafka 经验丰富
真正的存算分离 / 弹性扩缩 简单运维需求
异地多活、跨地域复制 日均 PB 级流处理
需要 Function Mesh 一站式 运维资源有限(Pulsar 三组件复杂)

11.5 业内厂商选型实例

公司 主流选型 原因
LinkedIn Kafka(自研 + 主导社区) 大数据日志主场景
Uber Kafka + Pulsar 混合 Kafka 流处理,Pulsar 多租户业务
腾讯 Kafka + Pulsar + TDMQ 业务多样,按场景选
阿里 RocketMQ(自研主推) 业务消息为主,RocketMQ 体感更佳
字节 Kafka 为主 + RocketMQ 业务侧 大数据 Kafka,订单/抖音电商 RocketMQ
美团 Mafka(Kafka 自研版) 兼容 Kafka 协议
滴滴 DDMQ(基于 RocketMQ + Kafka 自研) 业务隔离 + 流处理双需求
B站 RocketMQ + Kafka 弹幕用 RocketMQ,日志/视频流用 Kafka
Netflix Kafka + Keystone 大数据流处理
PayPal Kafka 主流 + 部分场景 RabbitMQ 金融可靠性

11.6 高频追问

Q:为什么阿里自研 RocketMQ 不直接用 Kafka?

A:Kafka 0.8 时代分区数超千就严重退化,RocketMQ(基于 Notify/MetaQ 演进)面向阿里百万级 Topic + 万级 IO 队列 + 业务消息场景设计:

  1. 单 CommitLog 解决多 Topic 写放大;
  2. 内置事务消息、延迟消息、重试机制(业务直接用,省去自研);
  3. 主从同步 + 选主独立设计,避免 ZK 依赖(早期 Kafka 强依赖 ZK);
  4. 双写双活 / NameServer 简化部署。

Q:Kafka 没有原生延迟消息怎么办?

A:方案优先级:

  1. 业务允许延迟近似:多 Topic 分桶(5s/30s/5m/30m/1h)+ 桶级消费 + sleep 到时刻 → 重投目标 Topic(精度低但简单)。
  2. 业务要求高精度:自研 KafkaDelayService → 消费 delay-topic → 写本地时间轮 → 到点重投目标 Topic(参考 Ch16.1 时间轮实现)。
  3. 能换技术栈:直接用 RocketMQ 5.0 任意精度延迟消息。
  4. 混合架构:业务消息走 RocketMQ,大数据流走 Kafka。

Q:消息精确一次(EOS)每个 MQ 怎么实现?

A:

MQ EOS 实现
Kafka Producer 幂等 + 事务 + Consumer read_committed + Flink 2PC Sink
RocketMQ 不支持严格 EOS,但事务消息 + 业务幂等可达成
RabbitMQ 不支持
Pulsar 类似 Kafka 的事务模型

Q:消息中间件的"丢消息"和"重复消费",本质是哪些参数决定的?

A:以 Kafka 为例(其他 MQ 类比):

  • acks / min.insync.replicas / unclean.leader.election / Consumer 提交时机;
  • :Producer 重试 + 是否启用幂等 / Consumer 处理后才提交。

金句 :所有 MQ 的底层语义都是 "at-least-once + 业务幂等 = exactly-once"。

Q:你们公司为什么选 X 而不是 Y?(自我介绍中常被问)

A:模板:

  1. 业务匹配度:举一个具体业务场景说明 X 比 Y 更适合;
  2. 生态成熟度:举团队工具链的依赖;
  3. 历史包袱:诚实说"团队已熟悉 X";
  4. 未来演进:能讲对 Y 的认知,不是不懂只是不选。

面试金句

"Kafka 是为吞吐而生,RocketMQ 是为业务消息而生,RabbitMQ 是为路由而生,Pulsar 是为多租户而生。订单履约用 RocketMQ 更省心;用户行为日志、CDC、Flink 流计算上游一定选 Kafka;银行间路由复杂用 RabbitMQ;SaaS 多租户用 Pulsar。"


12. 真实故障案例复盘(强烈建议背熟)

这是面试官最爱听的部分,建议每条都准备成 STAR(Situation-Task-Action-Result)讲法。

案例 1:消费积压百万,业务大盘 5 分钟数据延迟 1 小时

  • 现象 :监控告警 records-lag-max 飙升到 200w,业务大盘指标不更新。
  • 排查
    1. 看消费者机器负载,CPU/内存正常。
    2. 看消费者日志,发现频繁 CommitFailedException + Rebalance。
    3. 进一步发现 max.poll.interval.ms 默认 5 分钟,业务在 poll 后做了一次大批量 DB 写入,超时被踢出 Group。
  • 解决
    1. 临时:调大 max.poll.interval.ms 到 10 分钟;调小 max.poll.records 到 100,先止血。
    2. 长期:把消息处理改造为拉取线程 + 业务线程池模式,poll 线程只负责拉,业务线程池并行处理;引入手动提交 + 内存中按 partition 维护 offset 提交策略(避免乱序提交)。
    3. 顺手把分配策略从 RangeAssignor 改成 CooperativeStickyAssignor,减少 rebalance 影响。
  • 结果:积压 30 分钟内消化完,rebalance 次数从每天 50+ 降到 1 以内。

案例 2:Producer 发送毛刺,P99 从 20ms 飙到 2s

  • 现象:每天某几个时段 Producer P99 飙升,下游 RT 抖动。
  • 排查
    1. Broker 端 RequestQueueTimeMs P99 抖动同步飙高。
    2. 进一步看 LogFlushRateAndTimeMs 抖动 → 怀疑磁盘 IO。
    3. iostat 看磁盘 await 在抖动时段 > 1s,定位到同机的日志清理任务Topic compact 任务叠加触发。
  • 解决
    1. 把 compact 类 Topic 单独迁到独立 Broker。
    2. 调整 log.cleaner.threadslog.cleaner.io.max.bytes.per.second 限速。
    3. OS 层面 vm.dirty_background_ratio 从默认 10 调到 5,让脏页提前后台刷,避免突发集中刷盘。
  • 结果:P99 稳定在 30ms 内。

案例 3:消息丢失(ISR=1 时挂掉了)

  • 现象:财务对账发现 30 条订单消息丢失。
  • 排查
    1. Broker 日志显示前一晚 broker-2 宕机,broker-3 之前因 GC 超长被踢出 ISR。
    2. 此时 ISR 只剩 broker-1(Leader),且 min.insync.replicas=1,broker-1 写入返回 ack=all 仍认为成功。
    3. broker-1 也短时间宕机后,未同步到的消息永久丢失
  • 解决
    1. min.insync.replicas 从 1 改成 2,ISR 不足时 Producer 拒绝写入(业务侧降级到 fallback)。
    2. 副本数从 2 提到 3。
    3. 监控 UnderReplicatedPartitions 的告警阈值从 5 分钟降到 1 分钟。
    4. 关闭 unclean.leader.election.enable
  • 结果:之后再无丢失,可用性下降 < 万分之一。

案例 4:Rebalance 风暴

  • 现象:消费组 200+ 实例,每天发生几十次全员 Rebalance,每次中断 30s+。
  • 排查
    • 部分实例 GC 超 session timeout → 心跳超时被剔除 → Rebalance;
    • 老的 Eager 协议下一个掉队就全员 STW。
  • 解决
    • 升级到 Cooperative Sticky Assignor。
    • 调小 heartbeat.interval.ms(3s)+ 调大 session.timeout.ms(30s)。
    • JVM 调优解决 GC 抖动。
    • 配置 group.instance.idStatic Membership),实例重启不触发 rebalance。
  • 结果:日均 rebalance 从 50+ 降到 < 5。

案例 5:Lag 监控失真

  • 现象:Burrow 显示 Lag = 0,但下游用户反映数据延迟 30 分钟。
  • 排查 :业务消费者只 commit 了 offset 没真正处理(先提交后处理的反模式),Lag 自然为 0。
  • 解决:先处理后提交;同时在业务侧增加端到端时延埋点(消息时间戳 vs 业务落地时间),不依赖 Lag 一个指标。

案例 6:跨机房同步延迟(MirrorMaker 2)

  • 现象:MM2 同步上海→北京延迟 10 分钟。
  • 排查:MM2 任务并发不够 + 跨机房 RTT 高 + acks=all 导致 RT 放大。
  • 解决
    • 增加 MM2 task 数量到 = 源 Topic Partition 数。
    • Producer 端 compression.type=zstd 节省带宽 50%+。
    • 关键 Topic 升级专线。
    • Lag 报警分级:核心 Topic 30s,离线日志 5min。

12.7 按公司风格分组复盘(必背)

不同公司喜欢的故障案例方向有明显倾向,针对性背 1~2 个比泛泛而谈 6 个更高效。

公司风格 偏好案例 主要追问点
🟦 字节 案例 2(Producer 毛刺)、案例 4(Rebalance 风暴) "怎么定位是 GC / 磁盘 / 网络 / 锁?""有没有用 async-profiler?"
🟧 阿里 案例 1(消费积压)、案例 4(Rebalance 风暴) "你们 SLA 怎么定的?""故障演练如何?""降级方案?"
🟪 蚂蚁 案例 3(消息丢失)、案例 5(Lag 失真) "资损口径?""对账机制?""跨单元如何兜底?"
🟢 腾讯 案例 6(MM2 跨机房)、案例 1(积压) "百亿级 QPS 怎么撑?""带宽磁盘哪个先到顶?""成本优化空间?"
🟡 美团/快手 案例 4(Rebalance)、案例 5(Lag 失真) "外卖履约延迟?""实时计算反压?""灰度发布隔离?"

12.8 讲故障案例的"金牌口诀"

yaml 复制代码
S - Situation:  数据规模 + 业务影响 + 时间窗口
T - Trigger:    告警是什么 / 谁先发现的
A - Approach:   排查路径(不是直接抛结论,要展示方法论)
R - Resolution: 临时止血 + 长期根治分两步
M - Metrics:    具体数字(多少分钟修复 / 提升多少 / 节省多少)
P - Prevention: 复盘 + 监控加强 + 流程改进

SatRMP 一气呵成,比"我修了一个 bug"高 10 个段位。

例如案例 1 升级版讲法:

"S :我们订单消费组日均处理 5 亿消息、200+ 实例。某个周二上午 9 点,T :告警 records-lag-max 飙到 200 万,业务大盘指标 5 分钟数据延迟到 1 小时。A :先看消费者 CPU/内存正常排除资源瓶颈;看消费日志发现 CommitFailedException 频繁;继续查发现 max.poll.interval.ms 超时被 LeaveGroup → 触发 Rebalance → 处理被打断 → 进一步堆积。R :临时把 max.poll.interval.ms 从 5min 调到 10min + max.poll.records 从 500 降到 100 先止血;30 分钟内消化完积压。长期把消息处理改造为'拉取线程 + 业务线程池'模式,引入 CooperativeStickyAssignor。M :积压消化时间从 1h+ → 30min;日均 Rebalance 从 50+ 降到 < 5;消费 P99 从 8s 降到 800ms。P :补充 NumGroupsPreparingRebalance 监控;推动其他 200+ Group 平滑迁移到新模式;写了内部规范《Consumer 处理慢消息的 5 种正确姿势》。"



🧭 章节导航

🏠 返回 README

⬅️ 上一模块:01-基础与原理.md | ➡️ 下一模块:03-设计与编码.md

模块 文件
🔵 模块一·基础原理 01-基础与原理.md
🟢 模块二·调优运维(当前) 02-调优与运维.md
🟡 模块三·设计编码 03-设计与编码.md
🔴 模块四·源码 OS 04-源码与OS底层.md
🟣 模块五·分布式理论 05-分布式理论与大厂设计.md
📎 附录 99-附录.md
相关推荐
何陋轩1 小时前
Spring AI + RAG实战:打造企业级智能问答系统
后端·算法·设计模式
IT当时语_青山师__JAVA技术栈1 小时前
动态代理深度解析:JDK与CGLIB底层实现与实战
java·后端·面试
SamDeepThinking1 小时前
别人写的代码看不懂,到底是谁的水平有问题
java·后端·程序员
Nyarlathotep01131 小时前
类加载机制(3):类加载器
jvm·后端
Devin~Y1 小时前
大厂Java面试:Spring Boot + Redis/Kafka + Spring Cloud + JVM + RAG/向量检索(小Y翻车实录)
java·jvm·spring boot·redis·spring cloud·kafka·mybatis
苏三说技术1 小时前
小米二面:Redis为什么能支撑10万+ QPS?
后端
平凡但不平庸的码农2 小时前
Go 语言基础语法
开发语言·后端·golang
是宇写的啊2 小时前
SpringBoot 统一功能处理
java·spring boot·后端
等....2 小时前
Spring Boot多模块项目部署
java·spring boot·后端