监控MySQL主从复制,很多人习惯只看延迟时间,只要Seconds_Behind_Source是0就认为复制没问题。但实际上这个字段只能反映SQL线程回放进度与IO线程拉取进度之间的时间差,一旦IO线程已经断开,或者主库没有新写入,即便从库落后很多业务数据,这个值也可能停留在0。真正可靠的复制监控,需要同时观察线程运行状态、错误字段、GTID集合差异以及基于心跳的真实延迟。

一、从SHOW REPLICA STATUS读取复制健康指标
在MySQL 8.0.22及之后的版本中,推荐使用SHOW REPLICA STATUS查看复制状态;旧版本仍然兼容SHOW SLAVE STATUS。执行命令后,输出包含几十个字段,但日常监控中真正需要优先关注的是IO线程状态、SQL线程状态、延迟秒数、GTID执行集合和错误信息。
SHOW REPLICA STATUS\G
下面列出几个核心字段及其判断方式。
| 字段 | 含义与判断 |
|---|---|
Replica_IO_Running | IO线程状态,Yes表示正在从主库拉取二进制日志。Connecting表示正在尝试连接,No表示已停止。 |
Replica_SQL_Running | SQL线程状态,Yes表示正在回放中继日志。No通常说明遇到可执行错误,需要查看Last_Error。 |
Seconds_Behind_Source | SQL线程落后于IO线程的时间估算,为0不代表没有延迟,为NULL表示线程未运行或无法计算。 |
Retrieved_Gtid_Set | IO线程已经从主库接收到的GTID集合。 |
Executed_Gtid_Set | SQL线程已经在从库执行的GTID集合,与Retrieved_Gtid_Set的差值反映待回放事务。 |
Last_Errno 和 Last_Error | 最近一次SQL线程错误码和错误文本,排查复制中断的第一入口。 |
健康的复制状态应当满足:Replica_IO_Running和Replica_SQL_Running都为Yes,Seconds_Behind_Source为0或接近0,Last_Errno为0且Last_Error为空。只要有一个线程不是Yes,就应该立即告警,而不是等业务反馈读到旧数据。
二、Seconds_Behind_Source的局限与真实延迟计算
Seconds_Behind_Source的计算依赖事件中的时间戳。当SQL线程回放一个事务时,它会拿当前从库时间减去该事务在主库开始执行的时间。如果主从服务器时钟不同步,这个数值可能完全失真。时钟偏差较大的环境里,甚至可能算出负数或远大于实际延迟的数字。
更隐蔽的问题是IO线程停顿时,该字段也可能保持不变。因为SQL线程已经执行完手头的中继日志,没有新事件可算,自然也不会更新延迟值。此时从库表面上看是0秒延迟,但主库新事务根本没有传过来。对于这种场景,需要结合Retrieved_Gtid_Set与主库的gtid_executed进行对比,或者引入心跳表来持续测量真实链路延迟。
-- 查看从库已执行GTID与接收GTID SELECT @@GLOBAL.gtid_executed AS executed; SELECT @@GLOBAL.gtid_purged AS purged;
使用GTID对比时,可以在主库执行SELECT @@GLOBAL.gtid_executed,在从库执行同样的查询,然后比较两个集合的差异。差异集合里的GTID数量越多,说明积压越严重。虽然MySQL没有内置直接计算GTID差距的函数,但可以通过GTID_SUBTRACT等工具辅助分析。对于需要更精确的业务延迟,Percona Toolkit中的pt-heartbeat是更成熟的方案。
# 在主库创建并更新心跳表 pt-heartbeat --user=monitor --password=your_password --host=192.168.10.10 --database=percona --create-table --update --interval=1 --replace # 在从库持续监控延迟 pt-heartbeat --user=monitor --password=your_password --host=192.168.10.11 --database=percona --monitor --interval=1
pt-heartbeat会在主库的心跳表中记录一个时间戳,然后由从库回放这些心跳记录,通过当前从库时间减去心跳时间戳来得到实际延迟。这个方式不依赖服务器时钟完全一致,也能在IO线程短暂停滞时反映出真实的数据新鲜度,比Seconds_Behind_Source更适合作为告警指标。
三、用脚本自动化采集与告警
把复制状态监控做成定时任务,可以在异常发生时第一时间通知DBA。采集逻辑不需要很复杂:连接从库执行SHOW REPLICA STATUS,读取IO线程、SQL线程和延迟字段,根据阈值返回不同退出码。下面给一个Python示例。
import mysql.connector
conn = mysql.connector.connect(
host="127.0.0.1",
user="monitor",
password="your_password",
database=""
)
cursor = conn.cursor(dictionary=True)
cursor.execute("SHOW REPLICA STATUS")
row = cursor.fetchone()
if row is None:
print("CRITICAL: replica status is empty")
raise SystemExit(2)
io_running = row.get("Replica_IO_Running")
sql_running = row.get("Replica_SQL_Running")
delay = row.get("Seconds_Behind_Source")
if io_running != "Yes" or sql_running != "Yes":
print(f"CRITICAL: IO={io_running}, SQL={sql_running}")
raise SystemExit(2)
if delay is not None and delay > 30:
print(f"WARNING: replication delay {delay}s")
raise SystemExit(1)
print("OK: replication healthy")
这个脚本可以直接接入Zabbix、Nagios或Prometheus的文本采集器。阈值设置上,Seconds_Behind_Source超过30秒给警告,超过300秒给严重告警;线程状态不为Yes、Last_Errno非0,或者Retrieved_Gtid_Set长期不增长,都应该按照严重级别处理。
如果是Prometheus用户,推荐直接使用MySQL Exporter采集mysql_slave_status_seconds_behind_master等指标,再配置Alertmanager规则。但无论使用什么监控系统,建议把SHOW REPLICA STATUS原始输出保留在日志里,方便事后定位IO连接失败、SQL回放错误等具体原因。
四、复制异常排查与恢复思路
当监控发现IO线程不是Yes时,首先检查从库到主库的网络连通性,以及复制账号的密码、权限是否仍然有效。如果状态停留在Connecting,可以查看错误日志或使用SHOW REPLICA STATUS中的Last_IO_Errno和Last_IO_Error字段,通常能直接看到认证失败、连接拒绝或超时信息。
SQL线程停止是另一类高频问题。常见原因包括主键冲突、找不到要更新的行、DDL在从库执行失败等。此时Last_Errno和Last_Error会记录错误码和具体SQL。处理方式分两种:如果是允许跳过的事务,可以在传统位置模式下使用sql_slave_skip_counter,在GTID模式下注入空事务。示例:
-- 传统位置模式跳过一个事务 STOP REPLICA SQL_THREAD; SET GLOBAL sql_slave_skip_counter = 1; START REPLICA SQL_THREAD; -- GTID模式注入空事务 STOP REPLICA; SET GTID_NEXT='source_uuid:123'; BEGIN; COMMIT; SET GTID_NEXT='AUTOMATIC'; START REPLICA;
跳过事务虽然能快速恢复线程,但会造成主从数据不一致,必须随后校验相关表数据,并尽快修复差异。如果错误反复出现,说明从库可能被误写入,或者主从表结构不一致,需要进一步使用pt-table-checksum等工具定位数据偏差。
总的来说,MySQL复制监控不是看一眼延迟数字就够了。把线程状态、错误字段、GTID差距和心跳延迟组合起来,建立自动化采集和分级告警,才能在复制故障影响业务之前及时介入。
MySQL复制延迟复制状态监控SHOW REPLICA STATUS修改时间:2026-09-21 00:52:40