1. 问题背景:MySQL主从同步为何突然中断?
上周三凌晨2点15分,生产环境的MySQL从库突然报警,显示主从同步延迟超过300秒。作为DBA值班人员,我立即登录服务器检查状态。当时show slave status显示Slave_IO_Running=Yes但Slave_SQL_Running=No,错误信息提示"Could not execute Write_rows event on table db.order; Duplicate entry '178293' for key 'PRIMARY'"。
这种情况在电商大促期间并不罕见,但这次异常发生在业务低峰期,且主库没有明显的批量操作。更奇怪的是,这个主键冲突的记录在主库根本不存在!这让我意识到问题可能比表面看起来更复杂。
2. 排查路线图:从表象到根源的六步分析法
2.1 第一步:验证基础同步状态
首先确认基本同步参数是否正常:
SHOW VARIABLES LIKE 'server_id'; SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'binlog_format';发现从库的server_id居然与主库相同!这是第一个严重配置错误。虽然这不会直接导致主键冲突,但会导致网络分区时出现脑裂风险。立即用SET GLOBAL临时修改,并在my.cnf中永久修正。
2.2 第二步:解析冲突的relay log
通过mysqlbinlog工具分析出问题的relay log:
mysqlbinlog --base64-output=decode-rows -v /var/lib/mysql/relay-log.000123在position 471238位置发现了可疑的INSERT语句。该语句试图插入主键为178293的记录,但主库binlog中根本没有对应记录。这说明有"幽灵事件"混入了同步流。
2.3 第三步:GTID一致性校验
启用GTID跟踪后发现更诡异的现象:
SHOW GLOBAL VARIABLES LIKE 'gtid_mode'; SELECT * FROM mysql.gtid_executed;从库的gtid_executed表中存在一组不属于主库任何binlog的GTID(例如:a1b2c3d4-5e6f-7890-1234-567890abcdef:10000-15000)。这些"凭空出现"的事务正是导致冲突的元凶。
3. 深度分析:relay log污染的三重可能
3.1 可能性一:人为误操作
检查操作日志发现,有开发人员曾执行过:
STOP SLAVE; SET GLOBAL sql_slave_skip_counter=10; START SLAVE;这种暴力跳过错误的方式会破坏GTID连续性。更糟的是,该开发人员随后手动执行了数据修复脚本,这些操作被误记入从库的binlog(log_slave_updates=ON),形成污染源。
3.2 可能性二:网络分区导致的事件重复
在故障时间点,监控显示网络有3次30秒左右的闪断。MySQL 5.7的自动重连机制可能导致部分事件被重复传输。通过比对主从binlog的checksum值确认了重复事件的存在。
3.3 可能性三:备份恢复的副作用
排查发现,一周前做过从库的物理备份恢复。当时使用xtrabackup重建时,未正确清理原有的relay log文件,导致旧事件与新事件混合。这解释了那些"幽灵GTID"的来源。
4. 修复方案:五步完整恢复流程
4.1 第一步:建立数据基准点
在主库锁定相关表后,执行:
FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS;记录下File和Position值,同时在从库用mysqldump导出冲突表的结构和数据,形成恢复基准。
4.2 第二步:彻底重置复制环境
在从库执行完整清理:
STOP SLAVE; RESET SLAVE ALL; RESET MASTER; PURGE BINARY LOGS BEFORE NOW();删除所有relay log文件:
rm -f /var/lib/mysql/relay-log.*4.3 第三步:重建复制链路
使用正确的GTID自动定位方式重建复制:
CHANGE MASTER TO MASTER_HOST='master_host', MASTER_USER='repl_user', MASTER_PASSWORD='password', MASTER_AUTO_POSITION=1;4.4 第四步:数据一致性校验
使用pt-table-checksum进行全量校验:
pt-table-checksum --replicate=test.checksums h=master_host,u=check_user pt-table-sync --replicate test.checksums h=master_host,u=sync_user --print对差异数据生成修复脚本,经审核后执行。
4.5 第五步:防护措施加固
修改配置防止问题复发:
[mysqld] slave_parallel_workers=4 slave_parallel_type=LOGICAL_CLOCK sync_relay_log=1 sync_relay_log_info=1 slave_preserve_commit_order=15. 经验总结:主从同步的七个必知必会
GTID使用铁律:任何情况下都不要手动设置sql_slave_skip_counter,应该通过注入空事务处理冲突
备份恢复规范:使用xtrabackup重建从库时,必须添加--slave-info参数并清理旧relay log
网络隔离防护:配置master_retry_count和master_connect_retry控制重试行为
监控关键指标:除了Seconds_Behind_Master,还要关注Exec_Master_Log_Pos的推进情况
权限最小化:回收开发人员的STOP SLAVE权限,通过中间件控制
日志保留策略:主库binlog保留7天以上,从库relay log至少保留3天
定期校验机制:每周用pt-table-checksum做全量校验,关键业务表每天校验
这次事故的根本原因在于多重防护措施被绕过。事后我们建立了复制环境变更的checklist,任何涉及复制的操作都需要双人复核。同时优化了监控系统,当检测到server_id冲突、GTID断层等异常情况时,会自动触发告警并锁定复制账户。
关键教训:主从同步不是"配置完就忘"的服务,需要像对待数据库核心服务一样进行日常监控和维护。每次异常处理都应该形成闭环,把临时解决方案转化为长期防护机制。