3、源头排查:数据库日志解析延迟
聊完了架构层面的延迟分类,咱们得把目光聚焦到最源头——数据库本身。很多 CDC 延迟,说白了就是数据库这边“卡住了”。日志没生成,或者生成了但解析不出来,下游再怎么优化也是白搭。
我个人习惯,排查延迟的第一步,永远是先看数据库。不看源头,后面全是瞎猜。这一节,咱们就掰开揉碎,讲讲数据库日志解析延迟、负载影响,还有那个让人头疼的大事务。
3.1 日志解析延迟:Binlog 与 Redo Log 的“翻译”瓶颈
CDC 工具读取数据库变更,本质上是在“读日志”。MySQL 是 Binlog,Oracle 是 Redo Log。但日志不是给人类看的,它是二进制格式。CDC 工具需要把它“翻译”成我们能理解的行变更。
这个翻译过程,就是解析延迟的根源之一。
核心观点: 日志解析不是免费的。它需要 CPU 资源,需要内存,还需要和数据库本身“抢” I/O。
为什么会解析慢?我总结了三个常见场景:
- 日志格式不兼容: 比如 MySQL 的 Binlog 格式是 STATEMENT 或 MIXED,CDC 工具解析起来会比 ROW 格式更复杂,甚至可能出错。我在项目中遇到过,有人为了省空间用了 MIXED 格式,结果 Canal 解析时频繁报错,延迟直接飙到分钟级。
- 解析器性能瓶颈: 老版本的 CDC 工具,或者单线程解析模式,面对高并发写入时,解析速度跟不上日志生成速度。说白了,就是“嘴小吃不下”。
- 元数据频繁变更: 每次 DDL(比如加字段、改类型),CDC 工具都需要重新拉取表结构,和日志中的变更做映射。如果 DDL 很频繁,解析器会花大量时间在“对表结构”上,而不是真正解析数据。
| 日志类型 | 常见解析延迟原因 | 排查方向 |
|---|---|---|
| MySQL Binlog | 格式非 ROW、解析器单线程、DDL 频繁 | 检查 binlog_format、升级解析器版本、合并 DDL |
| Oracle Redo Log | LogMiner 性能、归档日志切换、补全日志缺失 | 开启 supplemental logging、优化 LogMiner 配置 |
我的小技巧: 排查解析延迟时,我会先看 CDC 工具的“解析队列深度”。如果队列一直在增长,说明解析速度跟不上生产速度。这时候别急着加机器,先看看是不是日志格式或 DDL 的问题。
3.2 数据库负载:当“生产者”本身就很吃力
数据库不是专门为 CDC 服务的。它要处理业务查询、写入、索引维护、备份……如果数据库本身负载就很高,那日志生成的速度自然会受影响。
你想想看,一个 CPU 使用率长期 90% 以上的数据库,它还有多少余力去快速刷日志?
数据库负载对延迟的影响,主要体现在这几个方面:
- CPU 争抢: 日志生成和解析都需要 CPU。如果业务查询把 CPU 吃满了,CDC 工具能分到的 CPU 时间片就少,解析自然变慢。
- I/O 瓶颈: 日志是要写磁盘的。如果磁盘 I/O 已经被业务写入占满,日志刷盘就会排队。日志刷得慢,CDC 工具能读到的数据就少。
- 锁竞争: 某些数据库在生成日志时,需要获取内部锁。如果业务有大查询长时间持有锁,日志生成也可能被阻塞。
曾经踩过的坑: 有一次,业务方在凌晨跑了一个全量报表,把数据库的 I/O 打满了。结果 Binlog 刷盘延迟从 1ms 涨到了 500ms,CDC 延迟直接爆表。从那以后,我建议所有核心业务库,都要给 CDC 工具预留独立的 I/O 带宽,或者至少做好限流。
怎么判断是不是数据库负载导致的延迟?我一般看三个指标:
- 数据库 CPU 使用率: 如果持续超过 80%,就要警惕了。
- 磁盘 I/O 等待时间(await): 如果超过 10ms,说明 I/O 有压力。
- 日志生成速率: 对比业务写入 TPS 和日志生成速率。如果 TPS 很高,但日志生成速率上不去,那大概率是数据库内部瓶颈。
3.3 大事务:延迟的“隐形杀手”
这是最容易被忽视,但影响最严重的问题。大事务,说白了就是一个事务里包含了大量数据变更。比如一次 UPDATE 影响 100 万行,或者一次 DELETE 删掉整个表。
为什么大事务会导致延迟?
因为日志是事务级别的。一个事务的所有变更,要么全部写入日志,要么全部不写。在事务提交之前,CDC 工具是看不到这些变更的。如果这个事务执行了 10 分钟,那这 10 分钟内的所有变更,对 CDC 来说就是“不可见”的。
一旦事务提交,CDC 工具会突然收到一个巨大的日志块。解析这个块需要大量 CPU 和内存,而且会阻塞后续日志的解析。结果就是:延迟瞬间飙升,然后慢慢恢复。
关键点: 大事务造成的延迟,不是线性的,而是“脉冲式”的。平时延迟很低,突然一下爆表,然后又降下来。这种波形,基本就是大事务的典型特征。
怎么排查大事务?我分享几个实战经验:
- 看 Binlog 文件大小: 如果某个 Binlog 文件特别大(比如超过 1GB),而且生成时间很短,那里面很可能包含大事务。
- 看 CDC 工具的“延迟曲线”: 如果延迟曲线是“尖刺状”,而不是“斜坡状”,优先怀疑大事务。
- 查数据库当前事务: 在 MySQL 里,可以用
SELECT * FROM information_schema.INNODB_TRX查看长时间未提交的事务。
-- 示例:查找执行时间超过 10 秒的事务
SELECT trx_id, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS trx_duration
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 10;
避坑指南: 我曾经遇到一个案例,业务方在循环里逐条 INSERT,但没有开启批量提交。结果一个事务里插入了 50 万条数据,事务执行了 3 分钟。提交后,CDC 延迟直接涨了 5 分钟。解决方案很简单:开启批量提交,或者把大事务拆成多个小事务。
为了让你更直观地理解这三者的关系,我画了一张图:
嗯,源头排查这块,说白了就是盯紧数据库。日志解析慢,就优化解析器或改格式;数据库负载高,就限流或扩容;大事务,就拆小或改业务逻辑。这三板斧下去,大部分源头延迟都能解决。