1. 问题现象
Spark 消费 Kafka 时,大部分节点处理耗时都在 6 秒以内,但是有一个节点每次都要 22 秒左右,日志中没有明显异常。
草稿中记录的现象如下:

这种问题比较麻烦,因为日志没有报错,任务也不是失败,只是某一个节点持续慢。
这类问题不能只看 Spark 应用日志,需要同时看:
- Spark UI 中 task 级别耗时。
- 慢节点所在 executor。
- executor 所在机器资源。
- Kafka partition 分布和 lag。
- 网络、磁盘、GC、CPU 等系统指标。
2. 先判断是节点慢还是数据慢
第一步不要直接怀疑 Kafka,也不要直接调 Spark 参数,先确认到底是“某台机器慢”,还是“某个 Kafka 分区数据量大”。
在 Spark UI 中查看对应批次的 Stage,重点看 Task 列表:
| 指标 | 重点 |
|---|---|
| Executor | 慢任务是否总在同一个 executor |
| Host | 慢任务是否总在同一台机器 |
| Duration | 是否稳定比其他 task 高 |
| Input Size / Records | 是否读取数据量明显更大 |
| GC Time | 是否存在明显 GC |
| Scheduler Delay | 是否调度等待时间高 |
| Getting Result Time | 是否结果回传慢 |
如果慢任务总在同一台机器,且数据量并不大,优先怀疑节点资源或网络问题。
如果慢任务不固定在同一台机器,而是固定在同一个 Kafka partition,优先怀疑 Kafka 分区数据倾斜。
3. 可能原因一:Kafka 分区数据倾斜
Spark 消费 Kafka 时,任务通常会按照 Kafka topic partition 拆分读取任务。如果某个 partition 的数据量明显比其他 partition 大,那么处理这个 partition 的 task 就会更慢。
查看 Kafka lag:
kafka-consumer-groups.sh \
--bootstrap-server kafka01:9092 \
--describe \
--group your_group
重点看:
TOPIC
PARTITION
CURRENT-OFFSET
LOG-END-OFFSET
LAG
CONSUMER-ID
HOST
如果某个 partition 的 LAG 或每批读取记录数明显更大,就需要检查 Kafka 写入端的 key 分布。
常见原因:
- producer 使用固定 key,导致数据打到少数 partition。
- 某些业务 key 热点明显。
- topic partition 数量太少。
- Spark 每批读取上限设置不合理。
Structured Streaming 可以通过 maxOffsetsPerTrigger 控制每批读取量:
spark.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "kafka01:9092")
.option("subscribe", "topic_name")
.option("maxOffsetsPerTrigger", "100000")
.load()
4. 可能原因二:Executor 所在机器资源异常
如果慢任务总是落到同一个 host,优先检查这台机器。
4.1 CPU
top
mpstat -P ALL 1
看是否存在:
CPU 使用率过高
iowait 过高
单核打满
4.2 内存和 GC
Spark 官方调优文档中也提到,GC 会影响任务执行时间。可以给 executor 打开 GC 日志:
--conf "spark.executor.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
然后到慢节点查看 executor 日志:
yarn logs -applicationId application_xxx | grep -i "Full GC"
如果慢节点存在频繁 Full GC,就需要调整 executor 内存、并发数或代码中的对象创建方式。
4.3 磁盘
iostat -x 1
df -h
重点看:
%util
await
iowait
磁盘剩余空间
Spark 任务如果发生 shuffle spill,磁盘慢会明显放大 task 耗时。
4.4 网络
sar -n DEV 1
ping kafka01
也可以从慢节点测试访问 Kafka broker:
nc -vz kafka01 9092
如果慢节点到 Kafka broker 网络延迟高、丢包、带宽打满,也会导致消费慢。
5. 可能原因三:Kafka Broker 分布不均
如果某个 Spark 节点总是从同一个 Kafka broker 拉数据,而这个 broker 本身压力大,也会造成局部慢。
可以查看 topic partition 分布:
kafka-topics.sh \
--bootstrap-server kafka01:9092 \
--describe \
--topic topic_name
关注:
Leader
Replicas
Isr
如果慢的 partition leader 集中在某个 broker,并且这个 broker 负载高,可以考虑:
- Kafka 分区重分配。
- 调整 partition leader 分布。
- 扩容 broker。
- 检查 broker 磁盘和网络。
6. 可能原因四:Spark 本地性和调度问题
Spark UI 中如果慢任务的 Scheduler Delay 很高,说明任务不一定是执行慢,也可能是调度等待慢。
可以看:
Locality Level
Scheduler Delay
Task Deserialization Time
Executor Run Time
如果是调度延迟,可以检查:
- executor 数量是否不足。
- 单个 executor core 是否设置太大。
- 是否有其他任务抢占资源。
- YARN 队列是否资源紧张。
Spark 官方文档也提到,Spark 程序的瓶颈可能来自 CPU、网络、内存等多个资源,不应该只从代码层面排查。
7. 推荐排查路径
我一般会按下面顺序排查:
7.1 在 Spark UI 定位慢 task
记录下面信息:
Stage ID
Task ID
Executor ID
Host
Duration
Input Records
GC Time
Scheduler Delay
7.2 判断慢是否固定在某台机器
如果每次都是同一台 host 慢,先查机器:
top
iostat -x 1
sar -n DEV 1
df -h
7.3 判断慢是否固定在某个 Kafka partition
查看 consumer group lag:
kafka-consumer-groups.sh \
--bootstrap-server kafka01:9092 \
--describe \
--group your_group
7.4 检查 Kafka broker
kafka-topics.sh \
--bootstrap-server kafka01:9092 \
--describe \
--topic topic_name
7.5 检查 GC
打开 GC 日志后观察慢节点:
yarn logs -applicationId application_xxx | grep -i "gc"
如果慢节点 GC 明显更高,就不是 Kafka 本身的问题。
8. 解决办法
不同原因对应不同解决方式:
| 原因 | 解决方式 |
|---|---|
| Kafka partition 数据倾斜 | 调整 producer key、增加 partition、限制每批读取量 |
| 某台 executor 机器慢 | 修复机器 CPU、磁盘、网络、内存问题,必要时下线节点 |
| Kafka broker 压力集中 | 重分配 partition,均衡 leader |
| GC 时间高 | 调整 executor 内存、core、序列化方式,减少对象创建 |
| 调度延迟高 | 增加资源,调整 executor 数量和 core 数 |
如果只是某一台节点持续慢,最直接的验证方式是临时把这台机器从 YARN NodeManager 或 Spark 可用节点中摘掉,再观察任务耗时是否恢复正常。
如果摘掉后耗时恢复,说明问题基本就在这台机器或它到 Kafka/HDFS 的链路上。
9. 总结
Spark 消费 Kafka 时,单个节点长期比其他节点慢,不应该只看应用日志。日志没有异常并不代表系统没有问题。
排查重点是先做归因:
是某台机器慢?
还是某个 Kafka partition 慢?
还是某个 broker 慢?
还是 Spark 调度或 GC 慢?
确认方向以后再处理,不要一上来就调 maxOffsetsPerTrigger 或 executor 参数。参数调整只能缓解问题,如果根因是节点网络、磁盘、GC 或 Kafka 分区倾斜,最终还是需要把这些基础问题修掉。