4. 源端监控实战(MySQL Binlog)
好,咱们进入实战环节。CDC 链路的第一站,就是 MySQL 的 Binlog。你想想看,如果源头的数据都抓不准、抓不全,后面整个链路就是空中楼阁。所以源端监控,我把它放在全链路监控的第一位。
这一章,我们聚焦四个核心指标:Binlog 生成速率、磁盘空间、主从延迟、解析异常。这四个点,是我在几十套生产环境里踩坑踩出来的。每一个都出过事故,每一个都有血的教训。
核心观点:源端监控不是看 Binlog 有没有,而是看 Binlog 生成得「快不快」、磁盘「够不够」、主从「同步是否及时」、解析「有没有报错」。缺一个,事故就在路上。
4.1 Binlog 生成速率监控
Binlog 生成速率,说白了就是 MySQL 每秒往磁盘上写了多少 Binlog。这个指标直接反映了数据库的写入压力。我见过一个案例,业务高峰期 Binlog 生成速率飙到 200MB/s,结果下游 Canal 消费不过来,Binlog 文件堆积,磁盘直接打满。
怎么监控?我一般用两种方式:
- 文件系统级: 通过
stat或du定时采集 Binlog 文件大小变化。 - MySQL 内部: 通过
SHOW MASTER STATUS的File和Position字段计算。
下面是我常用的监控脚本片段:
#!/bin/bash
# Binlog 生成速率监控脚本
# 每 60 秒采集一次
BINLOG_DIR="/var/lib/mysql"
INTERVAL=60
while true; do
# 获取当前 Binlog 文件大小
CURRENT_FILE=$(mysql -e "SHOW MASTER STATUS\G" | grep "File" | awk '{print $2}')
CURRENT_SIZE=$(stat -c%s "$BINLOG_DIR/$CURRENT_FILE")
sleep $INTERVAL
# 再次获取
NEW_SIZE=$(stat -c%s "$BINLOG_DIR/$CURRENT_FILE")
# 计算速率(字节/秒)
RATE=$(( (NEW_SIZE - CURRENT_SIZE) / INTERVAL ))
echo "$(date '+%Y-%m-%d %H:%M:%S') | Binlog: $CURRENT_FILE | Rate: $RATE bytes/s"
# 告警阈值:超过 50MB/s 触发
if [ $RATE -gt $((50 * 1024 * 1024)) ]; then
echo "WARNING: Binlog 生成速率过高!当前: $RATE bytes/s"
# 这里可以接入告警系统
fi
done
我的经验: 速率阈值要动态调整。白天业务高峰期 50MB/s 可能正常,凌晨 2 点 10MB/s 就算异常。我习惯按「基线 + 3σ」的方式设定动态阈值,而不是固定值。
4.2 Binlog 磁盘空间监控
这个监控点,说白了就是看磁盘会不会被 Binlog 撑爆。MySQL 的 Binlog 默认不会自动删除,除非你设置了 expire_logs_days 或 binlog_expire_logs_seconds。但即使设置了,如果生成速率突然暴增,磁盘还是可能被打满。
我记得有一次,某电商大促期间,Binlog 生成速率是平时的 10 倍。虽然设置了 7 天过期,但磁盘还是 3 天就满了。结果 MySQL 直接 Hang 住,因为写不了 Binlog 了。嗯,那次事故让我养成了一个习惯:磁盘使用率超过 80% 就要预警,超过 90% 必须立即处理。
监控命令很简单:
# 查看 Binlog 目录磁盘使用率
df -h /var/lib/mysql
# 查看 Binlog 文件列表及大小
ls -lh /var/lib/mysql/mysql-bin.*
# 查看 Binlog 过期设置
mysql -e "SHOW VARIABLES LIKE 'expire_logs_days';"
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';"
我建议的监控指标如下:
| 指标 | 正常范围 | 预警阈值 | 告警阈值 |
|---|---|---|---|
| 磁盘使用率 | < 60% | 80% | 90% |
| Binlog 文件数 | < 50 个 | 100 个 | 200 个 |
| 最大 Binlog 文件大小 | < 1GB | 2GB | 5GB |
| Binlog 过期时间 | 7 天 | < 3 天 | < 1 天 |
注意: 不要等到磁盘满了再处理。一旦 MySQL 因为写不了 Binlog 而 Hang 住,恢复起来非常麻烦。我曾经遇到过需要手动清理 Binlog 文件,然后重启 MySQL 的情况,那个过程简直是噩梦。
4.3 主从延迟监控
主从延迟,是 CDC 同步中最常见的坑。你想想看,如果主库写入了数据,但从库还没同步过来,CDC 工具(比如 Canal)连接的是从库,那它读到的 Binlog 就是滞后的。数据延迟就这么产生了。
监控主从延迟,最直接的方式就是看 Seconds_Behind_Master。但这个指标有个问题:它只反映 SQL 线程的延迟,不反映 IO 线程的延迟。也就是说,如果从库的 IO 线程已经落后了,但 SQL 线程还在拼命追,这个值可能显示为 0。
所以我一般会同时监控三个指标:
- Seconds_Behind_Master: 传统延迟指标,单位秒。
- Master_Log_File 和 Relay_Master_Log_File: 对比这两个文件是否一致,判断 IO 线程是否落后。
- Read_Master_Log_Pos 和 Exec_Master_Log_Pos: 对比这两个位置,判断 SQL 线程是否落后。
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Seconds_Behind_Master|Master_Log_File|Relay_Master_Log_File|Read_Master_Log_Pos|Exec_Master_Log_Pos"
输出示例:
Master_Log_File: mysql-bin.000123
Relay_Master_Log_File: mysql-bin.000123
Read_Master_Log_Pos: 456789012
Exec_Master_Log_Pos: 456789012
Seconds_Behind_Master: 0
如果 Master_Log_File 和 Relay_Master_Log_File 不一致,说明 IO 线程落后了。如果 Read_Master_Log_Pos 和 Exec_Master_Log_Pos 不一致,说明 SQL 线程落后了。
避坑指南: 我曾经遇到过一个案例,Seconds_Behind_Master 显示为 0,但数据就是延迟了 10 分钟。后来发现是因为从库的 IO 线程卡住了,但 SQL 线程已经把 Relay Log 消费完了,所以显示为 0。从那以后,我坚持同时监控文件位置,而不是只看秒数。
4.4 Binlog 解析异常监控
这个监控点,是 CDC 工具特有的。Binlog 本身是二进制格式,需要解析成可读的事件。如果解析过程中出现异常,比如 Binlog 文件损坏、格式不兼容、或者 MySQL 版本升级导致的事件格式变化,CDC 工具就会报错。
我常用的监控方式有两种:
- 工具日志监控: 比如 Canal 的
canal.log,Debezium 的connector.log。定期扫描日志中的 ERROR 和 WARN 级别日志。 - 心跳检测: 在源库创建一个心跳表,每隔几秒更新一条记录。CDC 工具如果能正常解析这条记录,说明链路正常。如果超过一定时间没收到心跳,说明解析可能出了问题。
下面是一个简单的日志监控脚本:
#!/bin/bash
# Binlog 解析异常监控脚本
LOG_FILE="/opt/canal/logs/canal.log"
KEYWORDS=("ERROR" "parse failed" "binlog checksum" "unknown event")
tail -f $LOG_FILE | while read line; do
for keyword in "${KEYWORDS[@]}"; do
if echo "$line" | grep -qi "$keyword"; then
echo "$(date) | 发现解析异常: $line"
# 触发告警
curl -X POST http://alert-server/api/v1/alert \
-d "source=canal&message=$line"
fi
done
done
重要提醒: Binlog 解析异常一旦发生,通常意味着数据丢失或数据错乱。不要只依赖日志监控,一定要配合心跳检测。我见过一个案例,Canal 日志一直正常,但实际已经停止解析新数据了,因为 Binlog 文件被意外 rotate 了。心跳检测能第一时间发现这种「静默故障」。
4.5 小结
源端监控,说白了就是盯住 Binlog 的「量」和「质」。量是生成速率和磁盘空间,质是主从同步和解析正确性。这四个监控点,缺一不可。