前言
MySQL 主从复制是生产环境中常用的高可用架构,通过将写操作同步到从库,实现数据冗余和读写分离。但主从延迟是一个让人头疼的问题:明明网络带宽充足,从库 CPU 也不高,为什么延迟就是降不下来?
很多人第一反应是"网络问题",但实际上主从延迟的原因五花八门,可能出在主库、从库、网络、配置等各个环节。本文从原理出发,详细分析主从延迟的各种原因,并给出排查路径和解决方案。
1 MySQL 主从复制原理
1.1 复制架构
MySQL 主从复制基于 binlog 实现,采用半同步或异步复制方式:
主库(Master) 从库(Slave) | | | 写入事务 | | --> 记录 binlog | | | | Dump Thread 发送 binlog ------> | | | I/O Thread 接收 | | --> 写入 relay log | | | | SQL Thread 读取 | | --> 执行 SQL | | | Commit | Apply
复制涉及的线程:
主库 Binlog Dump Thread:将从库的请求读取 binlog 并发送
从库 I/O Thread:连接主库,请求 binlog,写入 relay log
从库 SQL Thread:读取 relay log,执行 SQL
1.2 复制方式
异步复制:主库提交事务后立即返回客户端,不等待从库确认。默认模式。
半同步复制:主库提交事务后,等待至少一个从库确认收到 binlog 后才返回客户端。
-- 安装半同步插件 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; -- 启用半同步 SET GLOBAL rpl_semi_sync_master_enabled = 1; SET GLOBAL rpl_semi_sync_slave_enabled = 1; -- 查看配置 SHOW VARIABLES LIKE 'rpl_semi_sync%';
增强半同步(MySQL 8.0):等待从库应用并提交事务后才返回。
1.3 binlog 格式
STATEMENT:记录执行的 SQL 语句。优点是日志量小,缺点是某些函数(如 NOW()、UUID())在从库执行结果不同。
ROW:记录被修改的行。优点是结果准确,缺点是日志量大。
MIXED:混合模式。默认使用 STATEMENT,在需要时自动切换到 ROW。
-- 查看当前 binlog 格式 SHOW VARIABLES LIKE 'binlog_format'; -- 设置 binlog 格式(需要 SUPER 权限) SET GLOBAL binlog_format = 'ROW'; SET GLOBAL binlog_format = 'STATEMENT'; SET GLOBAL binlog_format = 'MIXED'; -- 配置文件设置 [mysqld] binlog_format = ROW
2 主从延迟监控
2.1 查看复制状态
-- 查看所有从库状态 SHOW SLAVE HOSTS; -- 查看从库复制状态(MySQL 5.7) SHOW SLAVE STATUSG -- 查看从库复制状态(MySQL 8.0,推荐使用) SHOW REPLICA STATUSG
关键指标说明:
-- MySQL 5.7 输出 *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.1.100 Master_User: repl_user Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000456 Read_Master_Log_Pos: 456789012 Relay_Log_File: relay-bin.000123 Relay_Log_Pos: 456789200 Relay_Master_Log_File: mysql-bin.000456 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: app_db Replicate_Ignore_DB: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 456789012 Relay_Log_Space: 789012345 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: a1b2c3d4-xxxx-xxxx-xxxx-xxxxxxxxxxxx Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: a1b2c3d4-xxxx:1-12345 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0 Network_Namespace:
关键字段详解:
| 字段 | 说明 | 正常值 |
|---|---|---|
| Seconds_Behind_Master | 延迟秒数 | 0 |
| Slave_IO_Running | I/O 线程运行状态 | Yes |
| Slave_SQL_Running | SQL 线程运行状态 | Yes |
| Master_Log_File | 主库 binlog 文件 | 与Read_Master_Log_Pos对应 |
| Read_Master_Log_Pos | I/O 线程读取位置 | 递增 |
| Relay_Master_Log_File | relay log对应的binlog | 与Exec_Master_Log_Pos对应 |
| Exec_Master_Log_Pos | SQL 线程执行位置 | 递增,与Read_Master_Log_Pos接近 |
| Last_Errno | 上次错误编号 | 0 |
| Last_Error | 上次错误信息 | 空 |
2.2 实时监控脚本
#!/bin/bash
# filename: check_replication_delay.sh
# 监控主从延迟
MYSQL_HOST="192.168.1.100"
MYSQL_PORT="3306"
MYSQL_USER="root"
MYSQL_PASS="your_password"
THRESHOLD=30 # 延迟阈值(秒)
while true; do
# 获取延迟秒数
DELAY=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS -e "SHOW SLAVE STATUSG" 2>/dev/null | grep "Seconds_Behind_Master" | awk '{print $2}')
if [ "$DELAY" = "NULL" ]; then
echo "$(date) [WARN] 复制线程未运行"
elif [ "$DELAY" -gt $THRESHOLD ]; then
echo "$(date) [ERROR] 延迟: ${DELAY}秒,超过阈值: ${THRESHOLD}秒"
# 显示详细状态
mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS -e "SHOW SLAVE STATUSG" 2>/dev/null | grep -E "(Slave_IO_Running|Slave_SQL_Running|Master_Log_File|Read_Master_Log_Pos|Exec_Master_Log_Pos|Relay_Log_Space|Last_Error)"
else
echo "$(date) [OK] 延迟: ${DELAY}秒"
fi
sleep 5
done
2.3 使用 Performance Schema 监控
-- MySQL 5.7+ 启用复制监控 -- 开启 performance_schema UPDATE mysql.general_log SET 'general_log' = 'ON'; -- 查看复制相关的表 USE performance_schema; -- 查看从库状态 SELECT * FROM replication_connection_statusG -- 查看应用延迟 SELECT * FROM replication_applier_statusG -- 查看协调事务状态 SELECT * FROM replication_applier_status_by_coordinatorG -- 查看 worker 事务状态 SELECT * FROM replication_applier_status_by_workerG -- 查看主库信息 SELECT * FROM replication_group_members; -- 查看慢应用的事务 SELECT * FROM replication_applier_status_by_worker WHERE LAST_ERROR_NUMBER != 0;
3 常见延迟原因与排查
3.1 网络问题
排查方法:
# 1. 测试主从网络延迟 ping -c 100 192.168.1.100 | tail -5 # 2. 测试主从网络带宽 iperf3 -c 192.168.1.100 # 3. 查看从库网络连接状态 mysql -e "SHOW PROCESSLIST;" | grep -E "Binlog Dump|Has sent all" # 4. 查看网络监控 ss -tan | grep 3306 netstat -i | grep eth0
解决方案:
检查主从之间的网络延迟和带宽
使用压缩减少数据传输量(slave_compressed_protocol=1)
如果是跨机房复制,考虑使用专线或优化网络拓扑
-- 启用压缩协议 SET GLOBAL slave_compressed_protocol = 1; -- 配置文件 [mysqld] slave_compressed_protocol = 1
3.2 主库写入压力大
排查方法:
-- 1. 查看主库的写入 QPS SHOW STATUS LIKE 'Com_insert'; SHOW STATUS LIKE 'Com_update'; SHOW STATUS LIKE 'Com_delete'; SHOW STATUS LIKE 'Questions'; -- 2. 查看主库的 binlog 写入速度 SHOW STATUS LIKE 'Binlog_disk_writes'; SHOW STATUS LIKE 'Binlog_stmt_cache_disk_use'; -- 3. 查看从库relay log写入速度 SHOW STATUS LIKE 'Relay_log_space'; -- 4. 查看主库的写入线程 SHOW PROCESSLIST;
解决方案:
优化主库写入,避免高峰期的批量写入
使用并行复制提升从库应用速度
调整 sync_binlog 和 innodb_flush_log_at_trx_commit 平衡性能和数据安全
-- 调整 sync_binlog(建议 100-1000) SET GLOBAL sync_binlog = 1000; -- innodb_flush_log_at_trx_commit: -- 1: 每次事务提交时刷新日志(最安全,性能最低) -- 2: 事务提交时写入日志,但不刷新,由操作系统决定 -- 0: 不在事务提交时刷新,由 master thread 决定 SET GLOBAL innodb_flush_log_at_trx_commit = 2;
3.3 从库 SQL 线程瓶颈
排查方法:
-- 1. 查看从库 SQL 线程状态 SHOW PROCESSLIST; -- 注意 State 列: -- "Reading event from the relay log" - 正常读取 -- "Slave has read all relay log..." - 追上主库了 -- "Waiting for an event from Coordinator" - 多线程复制协调器空闲 -- 其他状态可能有问题 -- 2. 查看等待的 Worker 线程 USE performance_schema; SELECT * FROM replication_applier_status_by_worker; -- 3. 查看 relay log 应用情况 SHOW STATUS LIKE 'Slave_retrieved_gtid_set'; SHOW STATUS LIKE 'Slave_executed_gtid_set';
常见问题:
单线程复制瓶颈:MySQL 5.6 以前的版本从库只有一个 SQL 线程,串行执行所有事件
大事务阻塞:主库的一个大事务在从库执行时,SQL 线程被阻塞
-- 查看主库正在执行的事务 SHOW ENGINE INNODB STATUS; -- 查看 History list length,如果很大说明有大事务在执行
解决方案:
升级到 MySQL 5.7+ 启用多线程并行复制
将大事务拆分成小事务
-- MySQL 5.7 多线程复制配置 [mysqld] slave_parallel_workers = 16 # 建议设置为 CPU 核心数的 50%-80% slave_parallel_type = LOGICAL_CLOCK # 基于组提交 slave_parallel_type = DATABASE # 基于数据库(需要配置 Replicate_Do_DB) -- 查看当前 worker 数量 SHOW VARIABLES LIKE 'slave_parallel_workers'; -- 查看多线程复制状态 SHOW SLAVE STATUSG -- 观察 Slave_SQL_Running_State 是否为 "Worker"
3.4 大事务延迟
问题现象:主库写入很快,但从库的延迟突然增加,然后慢慢减小。
排查方法:
-- 1. 查看从库 relay log 位置变化 -- 如果 Read_Master_Log_Pos 一直在增长,但 Exec_Master_Log_Pos 增长很慢,说明有大事务 -- 2. 监控 relay log 文件大小变化 SHOW MASTER STATUS; -- 主库 SHOW SLAVE STATUS; -- 从库,对比 Exec_Master_Log_Pos -- 3. 查看当前执行的事务 SHOW PROCESSLIST; -- 如果 State 显示 "Reading event from the relay log" 但长时间不变,说明正在执行大事务 -- 4. 查看 Innodb 事务状态 SHOW ENGINE INNODB STATUS; -- 关注 History list length 和 Lock wait time
解决方案:
拆分大事务:将 DELETE、UPDATE 大表的操作拆分成小批量
-- 错误的做法:一次性删除100万条 DELETE FROM large_table WHERE created_at < '2024-01-01'; -- 正确的做法:分批删除 DELETE FROM large_table WHERE created_at < '2024-01-01' LIMIT 10000; -- 循环执行直到删完
使用 pt-archiver 工具:Percona Toolkit 的 pt-archiver 可以安全归档和删除大表数据
# 安装 percona-toolkit yum install percona-toolkit -y # 使用 pt-archiver 删除数据(不锁表) pt-archiver --source h=localhost,D=test,t=large_table --where "created_at < '2024-01-01'" --limit 1000 --txn-size 1000 --purge --charset utf8
3.5 表缺少主键或唯一索引
问题原理:MySQL Row 格式复制时,如果表没有主键或唯一索引,DELETE 和 UPDATE 操作需要在从库全表扫描才能定位到对应的行。
排查方法:
-- 1. 查找没有主键的表
SELECT t.TABLE_SCHEMA, t.TABLE_NAME
FROM information_schema.TABLES t
JOIN information_schema.COLUMNS c
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.TABLE_NAME = c.TABLE_NAME
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
AND t.TABLE_TYPE = 'BASE TABLE'
AND t.AUTO_INCREMENT IS NOT NULL
GROUP BY t.TABLE_SCHEMA, t.TABLE_NAME
HAVING MAX(c.COLUMN_KEY) != 'PRI';
-- 2. 检查复制延迟与特定表的关系
-- 在主库执行可能产生大量 binlog 的操作,观察从库延迟变化
-- 3. 查看从库慢查询日志
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
解决方案:
为所有表添加主键
如果不能添加主键,至少添加一个唯一索引
-- 添加主键 ALTER TABLE your_table ADD PRIMARY KEY (id); -- 如果有业务逻辑的唯一字段组合,添加唯一索引 ALTER TABLE your_table ADD UNIQUE KEY uk_col1_col2 (col1, col2);
3.6 从库服务器负载高
排查方法:
# 1. 查看 CPU 使用率 top htop # 2. 查看磁盘 I/O iostat -x 1 iotop # 3. 查看内存使用 free -h # 4. 查看 InnoDB 缓冲池命中率 mysql -e "SHOW ENGINE INNODB STATUSG" | grep "Buffer pool hit rate" # 5. 查看连接数 mysql -e "SHOW STATUS LIKE 'Threads_connected';" mysql -e "SHOW STATUS LIKE 'Max_used_connections';"
解决方案:
将从库移出高负载服务器
增加从库硬件资源
优化从库查询(如果从库承担读请求)
-- 调整 InnoDB 缓冲池大小 SET GLOBAL innodb_buffer_pool_size = 10737418240; -- 10GB -- 调整 InnoDB 线程并发数 SET GLOBAL innodb_thread_concurrency = 32; -- 调整 I/O 线程数 SET GLOBAL innodb_read_io_threads = 16; SET GLOBAL innodb_write_io_threads = 16;
3.7 复制冲突
问题原理:主从复制是基于 binlog 的,在从库上执行的写操作(虽然不应该有)可能导致复制冲突。
排查方法:
-- 1. 查看复制错误 SHOW SLAVE STATUSG -- 查看 Last_Errno 和 Last_Error 字段 -- 2. 查看从库错误日志 SHOW VARIABLES LIKE 'log_error'; -- 查找 "Duplicate entry" 或 "Primary key exists" 等错误 -- 3. 查看从库上的写操作(不应该有) SHOW PROCESSLIST; -- 检查是否有非系统账号的写操作
解决方案:
确保没有应用连接从库执行写操作
使用 read_only 参数保护从库
-- 启用从库只读 SET GLOBAL read_only = ON; -- 允许有 SUPER 权限的用户写入(通常是管理员) SET GLOBAL super_read_only = ON; -- 配置文件 [mysqld] read_only = 1 super_read_only = 1
3.8 GTID 模式下的特殊问题
问题现象:MySQL 5.7+ 使用 GTID 复制时,某些情况下会出现延迟。
排查方法:
-- 1. 查看 GTID 执行状态 SHOW SLAVE STATUSG -- Retrieved_Gtid_Set: I/O 线程接收到的 GTID 集合 -- Executed_Gtid_Set: SQL 线程已执行的 GTID 集合 -- 2. 对比两者差异 -- Retrieved_Gtid_Set - Executed_Gtid_Set = 待应用的 GTID -- 3. 查看事务执行时间 SELECT * FROM performance_schema.events_statements_history ORDER BY TIMER_WAIT DESC LIMIT 10; -- 4. 查看是否有人在从库执行事务 SELECT * FROM performance_schema.replication_connection_statusG -- 关注 FREE_TREATS、GCT_RETRIEVED、HCT_RETRIEVED 等字段
常见问题与解决方案:
主库执行的事务被跳过:检查 slave_skip_counter 和 gtid_skip_counter
-- 跳过 N 个事务(慎用) SET GLOBAL gtid_skip_counter = 1; -- 正确做法:使用 gtid_executed 表管理
从库找不到事务:主库的 binlog 被 PURGE,但还没有同步到从库
-- 查看主库的 binlog 保留策略 SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'; SHOW VARIABLES LIKE 'expire_logs_days';
4 实战排查案例
4.1 案例一:从库延迟忽高忽低
现象:从库延迟在 0 到 300 秒之间波动。
排查过程:
# 1. 持续监控延迟变化
while true; do
echo "$(date): $(mysql -e 'SHOW SLAVE STATUSG' | grep Seconds_Behind_Master | awk '{print $2}')"
sleep 1
done
# 2. 观察主库写入模式
# 发现主库在每小时有批量导入操作,导致 binlog 突增
# 3. 检查主库的 binlog 生成速度
mysql -e "SHOW MASTER STATUS;"
# 每分钟观察 Binlog_Logpos 变化
# 4. 检查从库 SQL 线程状态
mysql -e "SHOW PROCESSLIST;"
# 发现 "Reading event from the relay log" 长时间不变
根因:主库每小时有批量导入,产生大量 binlog,从库 SQL 线程来不及应用。
解决:启用多线程并行复制,调整 slave_parallel_workers。
SET GLOBAL slave_parallel_workers = 16; SET GLOBAL slave_parallel_type = LOGICAL_CLOCK;
4.2 案例二:从库完全停止复制
现象:Seconds_Behind_Master 突然变为 0,但 Relay_Log_Pos 不再变化。
排查过程:
-- 1. 查看复制状态 SHOW SLAVE STATUSG -- 2. 发现 Slave_IO_Running = Yes, Slave_SQL_Running = No -- Last_Errno: 1062 -- Last_Error: Duplicate entry for key 'PRIMARY' -- 3. 查看错误详情 -- 尝试在主库执行相同语句 SELECT * FROM problem_table WHERE id = xxx;
根因:从库存在数据冲突,导致 SQL 线程停止。
解决:根据业务场景选择跳过错误或修复数据。
-- 方法1:跳过这个错误(适用于可以容忍重复的场景) STOP SLAVE; SET GLOBAL gtid_skip_counter = 1; START SLAVE; SET GLOBAL gtid_skip_counter = 0; -- 方法2:删除重复数据后重新复制 DELETE FROM problem_table WHERE id = xxx; START SLAVE; -- 方法3:使用 pt-table-checksum 检查主从数据一致性 pt-table-checksum h=master,u=root,p=pass h=slave,u=root,p=pass --replicate-check=1
4.3 案例三:延迟持续增加,追不上主库
现象:延迟从几秒开始,一直增长到几个小时。
排查过程:
-- 1. 检查 I/O 线程和 SQL 线程状态 SHOW SLAVE STATUSG -- Read_Master_Log_Pos 和 Exec_Master_Log_Pos 差距很大 -- 说明 I/O 线程接收速度快,但 SQL 线程执行慢 -- 2. 检查从库负载 SHOW PROCESSLIST; -- SQL 线程长时间处于某个状态 -- 3. 检查 relay log 大小 SHOW STATUS LIKE 'Relay_log_space'; -- 4. 检查是否有大事务 SHOW ENGINE INNODB STATUS; -- History list length 很大 -- 5. 检查慢查询 SHOW VARIABLES LIKE 'slow_query_log%';
根因:某个 UPDATE 语句在从库执行很慢(缺索引)。
解决:优化从库的查询性能。
-- 找到慢的 SQL,在主库执行 EXPLAIN 分析 EXPLAIN UPDATE problem_table SET status = 'done' WHERE updated_at < '2024-01-01'; -- 添加索引 ALTER TABLE problem_table ADD INDEX idx_updated_at (updated_at); -- 确认索引已应用 SHOW INDEX FROM problem_table;
4.4 案例四:网络正常但复制延迟
现象:主从之间 ping 延迟很低,但复制延迟很高。
排查过程:
# 1. 抓包分析主从复制流量 tcpdump -i eth0 port 3306 -w /tmp/replication.pcap # 2. 分析网络包 # 发现有很多小的 binlog 事件,而不是批量的大事件 # 3. 检查从库的 net_write_timeout 和 net_read_timeout mysql -e "SHOW VARIABLES LIKE 'net_%';"
根因:主库写入很频繁,但每个事务很小,binlog 传输和解析开销大。
解决:启用主库的 binlog_row_image 优化,并调整从库网络超时参数。
-- 使用 MINIMAL 格式记录行镜像(只记录修改的列) SET GLOBAL binlog_row_image = 'MINIMAL'; -- 增加网络超时时间 SET GLOBAL net_write_timeout = 300; SET GLOBAL net_read_timeout = 300; -- 启用压缩 SET GLOBAL slave_compressed_protocol = 1;
5 预防与优化
5.1 配置优化
# my.cnf 配置示例 [mysqld] # 复制相关 log-slave-updates = 1 slave-parallel-workers = 16 slave-parallel-type = LOGICAL_CLOCK slave-preserve-commit-order = 1 slave_compressed_protocol = 1 # binlog 优化 sync_binlog = 1000 binlog_format = ROW binlog_row_image = MINIMAL binlog_expire_logs_seconds = 604800 # 7天 # InnoDB 优化 innodb_flush_log_at_trx_commit = 2 innodb_buffer_pool_size = 16G innodb_thread_concurrency = 32 innodb_read_io_threads = 16 innodb_write_io_threads = 16 innodb_flush_method = O_DIRECT # 网络优化 net_write_timeout = 300 net_read_timeout = 300
5.2 监控告警
#!/bin/bash
# filename: replication_monitor.sh
# 主从复制监控告警脚本
MYSQL_HOST="192.168.1.100"
MYSQL_PORT="3306"
MYSQL_USER="monitor"
MYSQL_PASS="monitor_password"
ALERT_EMAIL="ops@example.com"
THRESHOLD_WARNING=30
THRESHOLD_CRITICAL=300
# 获取状态
STATUS=$(mysql -h$MYSQL_HOST -P$MYSQL_PORT -u$MYSQL_USER -p$MYSQL_PASS -e "
SHOW SLAVE STATUSG
" 2>/dev/null)
IO_RUNNING=$(echo "$STATUS" | grep "Slave_IO_Running:" | awk '{print $2}')
SQL_RUNNING=$(echo "$STATUS" | grep "Slave_SQL_Running:" | awk '{print $2}')
DELAY=$(echo "$STATUS" | grep "Seconds_Behind_Master:" | awk '{print $2}')
LAST_ERROR=$(echo "$STATUS" | grep "Last_Error:" | awk '{print $2}')
# 检查线程状态
if [ "$IO_RUNNING" != "Yes" ] || [ "$SQL_RUNNING" != "Yes" ]; then
echo "严重: 复制线程未运行" | mail -s "[CRITICAL] MySQL Replication Down" $ALERT_EMAIL
exit 1
fi
# 检查延迟
if [ "$DELAY" = "NULL" ]; then
echo "严重: 无法获取延迟信息" | mail -s "[CRITICAL] MySQL Replication Error" $ALERT_EMAIL
exit 1
fi
if [ $DELAY -ge $THRESHOLD_CRITICAL ]; then
echo "严重: 复制延迟 ${DELAY} 秒,超过临界值 ${THRESHOLD_CRITICAL} 秒" | mail -s "[CRITICAL] MySQL Replication Lag" $ALERT_EMAIL
elif [ $DELAY -ge $THRESHOLD_WARNING ]; then
echo "警告: 复制延迟 ${DELAY} 秒,超过阈值 ${THRESHOLD_WARNING} 秒" | mail -s "[WARNING] MySQL Replication Lag" $ALERT_EMAIL
fi
# 检查错误
if [ ! -z "$LAST_ERROR" ]; then
echo "发现复制错误: $LAST_ERROR" | mail -s "[ERROR] MySQL Replication Error" $ALERT_EMAIL
fi
5.3 定期检查脚本
-- filename: check_replication_health.sql -- 定期检查主从复制健康状态 -- 1. 检查复制线程状态 SELECT CASE WHEN Slave_IO_Running = 'Yes' AND Slave_SQL_Running = 'Yes' AND Seconds_Behind_Master = 0 THEN 'HEALTHY' WHEN Slave_IO_Running = 'Yes' AND Slave_SQL_Running = 'Yes' AND Seconds_Behind_Master > 0 THEN 'LAGGING' ELSE 'BROKEN' END AS replication_status, Seconds_Behind_Master, Master_Log_File, Read_Master_Log_Pos, Relay_Master_Log_File, Exec_Master_Log_Pos, Last_Error FROM information_schema.PROCESSLIST WHERE Command = 'Binlog Dump';
6 总结
6.1 延迟排查检查清单
| 检查项 | 命令/方法 |
|---|---|
| 延迟秒数 | SHOW SLAVE STATUS 中 Seconds_Behind_Master |
| I/O 线程状态 | Slave_IO_Running 是否为 Yes |
| SQL 线程状态 | Slave_SQL_Running 是否为 Yes |
| 网络延迟 | ping master_ip 和 iperf3 |
| 主库写入压力 | SHOW STATUS LIKE 'Com_%' |
| 从库负载 | top , iostat, free |
| 大事务 | SHOW ENGINE INNODB STATUS 的 History list length |
| 缺少索引 | EXPLAIN 慢查询 |
| 并行复制 | slave_parallel_workers 设置 |
6.2 常见延迟原因速查
| 原因 | 表现 | 解决方案 |
|---|---|---|
| 网络问题 | I/O 线程延迟 | 检查网络带宽和延迟 |
| 主库压力大 | 延迟与主库写入同步 | 优化主库写入,多线程复制 |
| SQL 线程瓶颈 | 单线程复制 | 启用并行复制 |
| 大事务 | 延迟突增后缓慢减小 | 拆分大事务 |
| 缺主键 | Relay_Log_Pos 不增长 | 添加主键或唯一索引 |
| 从库负载高 | 延迟持续增长 | 优化从库性能 |
| 复制冲突 | SQL 线程停止 | 修复冲突或跳过错误 |
6.3 优化建议
使用 GTID 简化复制管理:便于监控和问题定位
启用多线程并行复制:MySQL 5.7+ 推荐使用 LOGICAL_CLOCK 模式
使用半同步复制:确保事务至少到达一个从库
定期检查数据一致性:使用 pt-table-checksum
做好监控和告警:延迟超过阈值及时通知
主库写入优化:避免高峰期大批量写入
从库性能优化:合理配置 InnoDB 缓冲池和线程数
主从延迟不是单一原因造成的,需要从主库、从库、网络、配置等多个维度综合排查。养成良好的监控习惯,在问题发生时才能快速定位根因。
全部0条评论
快来发表一下你的评论吧 !