Skip to content

Kafka 常见故障处置 ​

按现象查。每条给:看什么、输出怎么读、根因在哪。

案例 1:消息积压持续增长 ​

现象:监控上 lag 从几百涨到几十万,业务侧"数据延迟"。

bash
B=10.0.0.31:9092
K=/opt/kafka/bin

$K/kafka-consumer-groups.sh --bootstrap-server $B --describe --group order-group
text
GROUP        TOPIC  PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG    CONSUMER-ID                    HOST         CLIENT-ID
order-group  order  0          120400          512400          392000 consumer-order-group-1-8f3a   /10.0.0.41   consumer-1
order-group  order  1          99800           499800          400000 consumer-order-group-2-a1b2   /10.0.0.42   consumer-2
order-group  order  2          135500          535500          400000 consumer-order-group-3-c3d4   /10.0.0.43   consumer-3

判断链条:

  1. 有活跃消费者(CONSUMER-ID 有值)但 lag 还在涨 → 消费速度 < 生产速度,是真实积压。
  2. 没有活跃消费者(全是 -)→ 消费服务挂了,先看进程。
  3. 消费者数 < 分区数 → 并行度不够。
bash
# 消费者数
$K/kafka-consumer-groups.sh --bootstrap-server $B --describe --group order-group --members

# 分区数
$K/kafka-topics.sh --bootstrap-server $B --describe --topic order | grep -c 'Partition:'
text
GROUP        CONSUMER-ID                    HOST          CLIENT-ID   #PARTITIONS
order-group  consumer-order-group-1-8f3a   /10.0.0.41    consumer-1  2
order-group  consumer-order-group-2-a1b2   /10.0.0.42    consumer-2  2
order-group  consumer-order-group-3-c3d4   /10.0.0.43    consumer-3  2

6

6 个分区、3 个消费者,每个 2 分区,并行度已满。要提吞吐只能:

手段前提代价
优化单条处理耗时无看日志找慢点(外部调用、同步写库)
批量化处理无改代码
扩分区 + 扩消费者不要求同 key 保序加分区会打乱 key 顺序
消费逻辑异步化位移要自己管复杂度上升

不要靠 --to-latest 掩盖积压

reset-offsets --to-latest 会丢弃全部积压消息。除非业务确认这批数据可以跳过,否则等于主动丢数据。

案例 2:leader 频繁切换,ISR 一直抖动 ​

现象:日志里大量 Partition order-3 marked as failed、Leader and ISR request,客户端偶尔超时。

bash
$K/kafka-topics.sh --bootstrap-server $B --describe --under-replicated-partitions
grep -Ei 'isr|leader|shrink|expand' /opt/kafka/logs/server.log | tail -30
text
	Topic: order	Partition: 3	Leader: 1	Replicas: 1,2,3	Isr: 1

[2026-10-27 20:11:02,441] INFO [Partition order-3 broker=1] Shrinking ISR from 1,2,3 to 1,2 (kafka.cluster.Partition)
[2026-10-27 20:11:04,882] INFO [Partition order-3 broker=1] Shrinking ISR from 1,2 to 1 (kafka.cluster.Partition)
[2026-10-27 20:11:31,110] INFO [Partition order-3 broker=1] Expanding ISR from 1 to 1,2 (kafka.cluster.Partition)

ISR 反复收缩又恢复,通常三个原因:

1. 副本拉取超时(最常见) ​

bash
grep -Ei 'replica.*(timeout|lag|fetch)' /opt/kafka/logs/server.log | tail -10
text
WARN [ReplicaFetcher replicaId=2, leaderId=1] Error in response for fetch request (kafka.server.ReplicaFetcherThread)
java.net.SocketTimeoutException: Read timed out

对策(broker 配置):

ini
replica.fetch.max.bytes=1048576
replica.socket.timeout.ms=30000
replica.fetch.wait.max.ms=500
replica.lag.time.max.ms=30000
num.replica.fetchers=4

num.replica.fetchers 从默认 1 调到 4,对多分区场景提升明显。

2. broker GC 停顿 ​

bash
jstat -gcutil $(jps | grep Kafka | awk '{print $1}') 1000 5
text
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
  0.00   0.00  12.30  45.20  94.10  91.20    842    8.234     3    6.120   14.354

FGCT 6 秒、FGC 3 次,说明有 Full GC。堆太大或太大对象导致。Kafka 推荐 6~8G 堆,再大 GC 停顿反而变长(它主要靠页缓存,不需要大堆)。

ini
# /opt/kafka/bin/kafka-server-start.sh 里或环境变量
export KAFKA_HEAP_OPTS="-Xmx6G -Xms6G -XX:+UseG1GC -XX:MaxGCPauseMillis=20"

3. 磁盘 IO 饱和 ​

bash
iostat -x 1 3
text
Device   r/s     w/s     rkB/s     wkB/s  await  %util
vda     120.4  1840.2   4816.0   147216.0   8.2   98.4

%util 98%、await 8ms → 磁盘打满。Kafka 是顺序写,正常 await 应在 1~3ms。换 SSD、拆盘、降保留时长。

案例 3:磁盘写满,broker 挂掉 ​

现象:broker 突然退出,日志 java.io.IOException: No space left on device。

bash
df -h /data
du -sh /data/kafka/logs
/opt/kafka/bin/kafka-log-dirs.sh --bootstrap-server $B --describe --topic-list order | head -20
text
Filesystem      Size  Used Avail Use% Mounted on
/dev/vdb        500G  500G     0 100% /data

120G	/data/kafka/logs

先救火:删过期数据。

bash
# 临时缩短某个大 topic 的保留时间(会立刻触发清理)
$K/kafka-configs.sh --bootstrap-server $B --entity-type topics --entity-name order \
  --alter --add-config retention.ms=3600000

# 手工触发一次清理检查(不用重启)
$K/kafka-configs.sh --bootstrap-server $B --entity-type topics --entity-name order \
  --alter --add-config segment.ms=3600000

等 5 分钟(log.retention.check.interval.ms)看磁盘是否下降。

如果还是不动,说明segment 粒度问题:一个 1GB 的 segment 只有整段过期才会被删。调小:

ini
log.segment.bytes=268435456      # 256MB

注意 log.segment.bytes 是新建 segment 生效,已有的大 segment 不会自动切小。

别直接删 log.dirs 里的文件

rm -rf /data/kafka/logs/order-3 会让 broker 认为这个分区文件丢失,重建时可能触发数据不一致,甚至起不来。只能通过 retention 或 kafka-delete-records.sh 删。

长期措施:

措施说明
设 retention.bytes每个分区上限,防止单 topic 吃满盘
加盘log.dirs 支持多目录,Kafka 会自己均衡
分层存储(Tiered Storage)3.6+ 支持把老数据放对象存储
监控 %util 和磁盘使用率70% 就告警,别等 100%

案例 4:时间不同步导致消息"消失" ​

现象:按时间消费 --offset-for-times 拿不到预期数据;或者消息刚写进去就被删了。

bash
timedatectl
chronyc tracking
date -u
text
               Local time: Tue 2026-10-27 20:31:04 CST
           Universal time: Tue 2026-10-27 12:31:04 UTC
                 Time zone: Asia/Shanghai (CST, +0800)
               NTP enabled: yes
NTP synchronized: no

Reference ID    : 00000000 ()
Stratum         : 0
System time     : 0.000000000 seconds fast of NTP time

NTP synchronized: no 就是问题。三台 broker 时间差超过几十秒时:

  • retention.ms 按segment 最后修改时间判断,时间快的机器可能提前删数据。
  • 按时间查偏移量算错。
  • 事务超时、log.roll.hours 行为异常。
bash
# 开时间同步
timedatectl set-ntp true
chronyc sources -v
text
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^* 10.0.0.1                    3   6   377    32    -12us[  -45us] +/-  1.2ms

^* 表示已同步。差值是微秒级就正常。

容器里时间不同步更隐蔽

宿主机同步了,容器默认共享宿主机的时钟,一般没问题。但如果容器有独立时间命名空间(某些安全沙箱),date 会漂移。部署时检查容器内 date 与宿主机差多少。

案例 5:broker 起不来 ​

现象:systemctl start kafka 失败,或者起来几秒就退出。

bash
systemctl status kafka --no-pager
journalctl -u kafka -n 50 --no-pager

常见五类错误:

日志根因处置
Cluster ID mismatch(KRaft)meta.properties 的 cluster id 与配置不符三台统一格式化,或换回原 log.dirs
Log directory ... is not empty对已有数据的目录执行 format确认数据可弃后清空,否则恢复原目录
Address already in use端口被占(旧进程没杀干净)ss -lntp | grep 9092,杀进程
Inconsistent clusterId混用了不同集群的元数据目录恢复备份或重新格式化
java.lang.OutOfMemoryError: Map failed内存不足或 vm.max_map_count 太小加内存;sysctl -w vm.max_map_count=262144
bash
ss -lntp | grep -E '9092|9093'
ps -ef | grep -i kafka | grep -v grep
text
LISTEN 0  50  10.0.0.31:9092  0.0.0.0:*  users:(("java",pid=19233,fd=140))

有残留进程先 kill -15,等 30 秒再 kill -9。别直接 -9,会留下没关闭的日志文件和锁。

案例 6:确认消息是否丢失 ​

现象:下游说少了数据,要定位是"没发"、"没写进去"还是"消费跳过了"。

第一步:确认 broker 上有没有 ​

bash
# 看分区的最新位移
$K/kafka-get-offsets.sh --bootstrap-server $B --topic order --time -1
text
order:0:512400
order:1:499800
order:2:535500

和故障前的数字对比,没涨说明生产者没写进来。

第二步:确认消息真的在磁盘上 ​

bash
$K/kafka-dump-log.sh --files /data/kafka/logs/order-0/*.log \
  --print-data-log 2>/dev/null | grep -c 'payload:'

或者按业务字段搜:

bash
$K/kafka-dump-log.sh --files /data/kafka/logs/order-0/*.log \
  --print-data-log 2>/dev/null | grep 'orderId-88213'

只能查还在保留期内的数据

超过 retention.ms 的数据已经被物理删除,dump-log 也找不到。要查历史只能靠下游数据库或归档。

第三步:确认消费没跳过 ​

bash
$K/kafka-consumer-groups.sh --bootstrap-server $B --describe --group order-group

如果 CURRENT-OFFSET 在故障期间跳变(比如从 1200 直接到 1400),说明有 seek() 或 reset-offsets 操作跳过了消息。

常见的丢数据原因 ​

原因特征
acks=1 + leader 挂生产端显示成功,broker 上不存在
unclean.leader.election.enable=true日志里有 UncleanLeaderElectionsPerSec,旧数据被覆盖
消费端自动提交 + 异步处理CURRENT-OFFSET 超过实际处理进度
retention 太短消息在消费前就被删了
生产者缓冲未 flush 就退出最后一批没发出去

案例 7:生产者超时、消息太大 ​

现象:TimeoutException: Expiring N record(s) for order-3:30000 ms has passed 或 RecordTooLargeException。

java
// 超时类
org.apache.kafka.common.errors.TimeoutException: Expiring 1 record(s) for order-3:
30000 ms has passed since batch creation

Expiring 表示消息在客户端缓冲区待了 30 秒还没发出去。原因:

原因判断
broker 不可达kafka-broker-api-versions.sh 能否列出
分区 leader 不可用--unavailable-partitions 有输出
buffer.memory 满生产者端 buffer-available-bytes 接近 0
消息太大被 broker 拒日志里有 RecordTooLargeException

消息太大要三层一起改:

bash
# broker
echo 'message.max.bytes=5242880'    # 5MB
echo 'replica.fetch.max.bytes=5242880'

# topic
$K/kafka-configs.sh --bootstrap-server $B --entity-type topics --entity-name order \
  --alter --add-config max.message.bytes=5242880

# 消费者
# fetch.max.bytes >= 5MB,max.partition.fetch.bytes >= 5MB

不要为了大消息把上限调到几十 MB

单条消息 10MB 会让批量发送失效、复制变慢、内存压力大。正确做法是把大 payload 放对象存储,Kafka 里只传 URL 或 ID。

案例 8:消费者卡住不动,日志刷 rebalance ​

现象:lag 不降,日志每几分钟出现一次 Revoking previously assigned partitions。

bash
grep -Ei 'rebalance|revoking|max.poll|heartbeat' /var/log/order-service/app.log | tail -20
text
2026-10-27 20:40:11 INFO  [order-group] Revoking previously assigned partitions [order-0, order-1]
2026-10-27 20:40:11 INFO  [order-group] (Re-)joining group
2026-10-27 20:45:31 WARN  [order-group] consumer poll timeout has expired. This means the time between
subsequent calls to poll() was longer than the configured max.poll.interval.ms

max.poll.interval.ms 超时。要么单批处理太慢,要么处理线程卡死。

bash
# 看线程栈,确认卡在哪
jstack <pid> | grep -A 20 'kafka-coordinator-heartbeat\|order-consumer'

常见卡点:

  • 同步调用下游 HTTP,对方超时 30 秒。
  • 同步写数据库,锁等待。
  • 大批量处理,max.poll.records=500 太大。

治标:

yaml
spring:
  kafka:
    consumer:
      max-poll-records: 100
      properties:
        max.poll.interval.ms: 600000

治本:把单批处理时间压到 1 分钟以内,或者改成"poll 后立即入本地队列 + 线程池处理 + 手动提交"。

异步化后不能再用自动提交

一旦处理异步化,poll() 返回时消息还没处理完。自动提交会提交未完成的位置,进程一挂全丢。必须手动 commitSync(),在异步任务完成后按水位提交。

案例 9:UnknownTopicOrPartitionException ​

现象:生产者或消费者报 topic 不存在,但 --list 里明明有。

bash
$K/kafka-topics.sh --bootstrap-server $B --list | grep '^order$'
$K/kafka-topics.sh --bootstrap-server $B --describe --topic order
text
order

Topic: order	TopicId: 7cQ3nR8vW1xY4zA6bC9dEf	PartitionCount: 6	ReplicationFactor: 3	Configs:
	Topic: order	Partition: 0	Leader: 3	Replicas: 3,1,2	Isr: 3,1,2

四个原因:

原因判断
客户端连错集群检查 bootstrap.servers,两台机器可能连了不同环境
分区数未就绪(刚创建)等 1~2 秒重试,或看 controller 日志
auto.create.topics.enable=false 且 topic 真不存在拼写/大小写错误
元数据缓存过期客户端重启,或等 metadata.max.age.ms

刚创建就写常见于自动化脚本:kafka-topics.sh --create 返回成功,但 /brokers/topics 在 ZK/KRaft 里还没传播到所有 broker。加个重试即可。

快速对照表 ​

现象第一步常见根因
lag 增长consumer-groups --describe消费慢、消费者挂、并行度不足
客户端超时--unavailable-partitionsleader 不可用、ISR 不足
ISR 抖动grep Shrinking ISR网络、GC、磁盘 IO
磁盘满df -h + log-dirsretention 配置、segment 太大
broker 起不来journalctl -u kafka端口占用、cluster id、meta.properties
消息重复检查提交时机rebalance、处理完未提交、自动提交
消息丢失kafka-get-offsets + dump-logacks=1、unclean 选举、retention 太短
消费卡住jstack + rebalance 日志max.poll.interval 超时
topic 不存在--describe连错集群、拼写、元数据未传播