3. 数据源接入故障:Kafka 消费延迟、Binlog 同步中断、JDBC 连接池耗尽
数据源接入层,说白了就是实时特征平台的「水源地」。水源出了问题,下游整个管道都得干瞪眼。我排查过不少线上事故,十次里有七八次都跟这三类故障有关:Kafka 消费卡住、Binlog 同步断了、JDBC 连接池被撑爆。咱们一个一个拆开聊。
3.1 Kafka 消费延迟:不只是「慢」那么简单
Kafka 消费延迟,最直观的表现就是消息积压。你去看 Consumer Lag,发现已经几百万条了。这时候别慌,先问自己三个问题:是生产端写太快?还是消费端处理太慢?还是中间出了什么幺蛾子?
我个人习惯先查 Consumer Group 的状态。用 kafka-consumer-groups.sh --describe 看一眼,如果发现分区分配不均匀,或者有成员频繁加入退出,那大概率是 Rebalance 在作祟。
kafka-consumer-groups --describe --group your_group --members --verbose 看每个消费者的分区分配情况。如果某个消费者分配了 10 个分区,另一个只分了 1 个,明显不均衡。
我在项目中遇到过一回,线上 Kafka 消费延迟从 10 秒飙到了 15 分钟。查了半天,发现是某个同事把 max.poll.records 设成了 5000,但单条消息处理时间平均要 200ms。你算算,5000 × 200ms = 1000 秒,一次 poll 就要处理 16 分钟,不延迟才怪。
常见的 Kafka 消费延迟根因,我整理了一张表:
| 现象 | 可能原因 | 快速定位命令 |
|---|---|---|
| Lag 持续增长 | 消费能力不足 / 分区数过少 | kafka-consumer-groups --describe |
| Lag 忽高忽低 | Rebalance 频繁 / GC 停顿 | 查看消费者日志中的 rebalance 事件 |
| 部分分区 Lag 高 | 分区分配不均 / 某分区数据倾斜 | --members --verbose 查看分配 |
| 消费端报错 | Offset 提交失败 / 序列化异常 | 查看 commit offset failed 日志 |
session.timeout.ms 设得太小(比如 3 秒),结果消费端稍微 Full GC 一下就被踢出组,触发 Rebalance。Rebalance 期间所有分区都停止消费,Lag 瞬间暴涨。建议生产环境至少设到 30 秒以上。
3.2 Binlog 同步中断:数据管道「断流」了
Binlog 同步,说白了就是实时特征平台从数据库「偷」数据的方式。一旦中断,下游特征全部停摆。我见过最惨的一次,Binlog 断了 6 小时没人发现,恢复后补数据补了整整一天。
为什么会中断?原因其实很固定:
- GTID 不一致:主从切换后,Canal 或 Debezium 记录的 GTID 和新的主库对不上,直接罢工。
- 磁盘空间满:Binlog 文件把磁盘撑爆了,MySQL 直接拒绝写入,同步自然断。
- 网络闪断:Canal 和 MySQL 之间的长连接断了,重连机制没生效。
- Binlog 文件被清理:
expire_logs_days设得太短,Canal 还没消费完就被删了。
我个人习惯在 Canal 的日志里搜 EOFException 或 SocketException,这两个关键字基本能覆盖 80% 的中断场景。另外,记得监控 Canal 的 delay 指标,一旦超过阈值立刻告警。
binlog_expire_logs_seconds 建议设为 604800(7 天),Canal 的 canal.instance.master.journal.name 和 canal.instance.master.position 要定期备份,防止重启后找不到位置。
嗯,这里要注意一个细节:Binlog 同步中断后,恢复时一定要确认 GTID 或 Position 的连续性。我曾经跳过这个步骤,结果数据出现了重复和缺失,排查了整整两天才发现是 GTID 回滚导致的。
3.3 JDBC 连接池耗尽:无声的「雪崩」
JDBC 连接池耗尽,是实时特征平台里最隐蔽的故障之一。表面上看,业务接口响应变慢,数据库 CPU 正常,但就是拿不到连接。你想想看,连接池里的连接都被占着不放,新的请求只能排队等超时。
我遇到过最典型的一次:某个实时特征计算任务,每次处理都要查一次 MySQL,但查询特别慢(3 秒以上)。并发一上来,20 个连接全被占满,后续请求全部超时。更坑的是,超时后连接没有正确归还,直接泄漏了。
排查 JDBC 连接池问题,我一般按这个顺序来:
- 看监控:连接池的
active数是否等于maxActive?waiting数是否持续增长? - 查慢查询:MySQL 的
slow_query_log里有没有长时间未返回的 SQL? - 验代码:检查
try-with-resources或finally块是否确保连接归还。 - 调参数:
maxActive、maxWait、removeAbandonedTimeout是否合理?
maximumPoolSize 建议设为 CPU核心数 × 2 + 1,connectionTimeout 设为 3000ms,idleTimeout 设为 600000ms。别贪多,连接数不是越大越好,太多反而增加数据库负担。
我曾经踩过一个坑:某个实时任务用了 Druid 连接池,removeAbandonedTimeout 设成了 60 秒,但业务 SQL 执行时间偶尔会超过 60 秒。结果连接被 Druid 强制回收,但业务线程还在用,直接报了 Connection is closed 异常。后来我把超时时间改成了 300 秒,同时优化了 SQL 性能,问题才彻底解决。
maxActive。加连接数只是治标,治本要优化 SQL 和代码。
3.4 故障排查的「三板斧」
说了这么多,总结一下我自己的排查套路。遇到数据源接入故障,别慌,按这三步走:
- 第一板斧:看监控。Kafka Lag、Binlog Delay、连接池 Active 数,这三个指标能告诉你「哪里疼」。
- 第二板斧:查日志。消费端日志、Canal 日志、应用日志,搜关键字
error、exception、timeout、rebalance。 - 第三板斧:调配置。根据定位到的根因,调整消费参数、连接池参数、同步参数。每次只改一个变量,观察效果。
说白了,数据源接入故障不可怕,可怕的是没有排查思路。你只要把监控搭好、日志收全、配置参数理解透,大部分问题都能在 10 分钟内定位。嗯,剩下的 10% 嘛……那就得靠经验和运气了。