主从切换必须严格按步骤验证:先停写并刷日志(FLUSH LOGS等),记录准确位点;再提升从库为新主(RESET SLAVE ALL + read_only=OFF);最后让其他从库指向新主的SHOW MASTER STATUS值,并全链路验证数据一致性与业务可用性。

确认主库已停止写入并刷新日志
主从切换前最常被跳过的一步:没等主库把所有 binlog 都刷到磁盘,就直接停服务。这会导致从库追不到最新事务,切过去后数据丢失。
必须执行 FLUSH LOGS 和 FLUSH BINARY LOGS,再查 SHOW MASTER STATUS 记下当前 File 和 Position —— 这两个值是后续让新主库“对齐旧主库最后状态”的唯一依据。
- 别只看
SHOW SLAVE STATUS\G里的Exec_Master_Log_Pos,它反映的是从库已执行的位置,不是主库当前写入位置 - 如果主库还能连上,用
SELECT @@global.gtid_executed(GTID 模式下)比靠 file/pos 更可靠 - 停主库前务必确认应用层已切断写流量,否则可能有连接正在提交事务但未落盘
将原从库提升为新主库(RESET SLAVE ALL + 只读关闭)
很多人在 STOP SLAVE 后直接开写,结果发现 read_only=1 还开着,或者残留的 relay_log 文件干扰后续 binlog 解析。
标准操作是先 STOP SLAVE,再执行 RESET SLAVE ALL —— 注意是 ALL,它会清掉 master.info、relay-log.info 和所有 relay log 文件,避免下次误启复制。
-
SET GLOBAL read_only = OFF必须执行,且建议检查SELECT @@read_only确认为0 - 如果用了 GTID,记得确认
gtid_mode = ON且enforce_gtid_consistency = ON已生效 - 不要跳过
RESET SLAVE ALL改用RESET SLAVE,后者不删文件,容易在后续故障恢复中引发 position 错乱
让其他从库指向新主库(CHANGE MASTER TO 的关键参数)
这里最容易出错的是 MASTER_LOG_FILE 和 MASTER_LOG_POS 填错了——不是填原主库的当前值,而是填“新主库”执行 SHOW MASTER STATUS 返回的值。
因为新主库刚由从库升上来,它的 binlog 是从原主库同步来的 relay log 转换而来,起始位置未必和原主库一致。必须以它自己的 SHOW MASTER STATUS 输出为准。
- 命令示例:
CHANGE MASTER TO MASTER_HOST='new-master-ip', MASTER_USER='repl', MASTER_PASSWORD='xxx', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000007', MASTER_LOG_POS=154; - GTID 模式下改用:
CHANGE MASTER TO MASTER_AUTO_POSITION = 1;,此时不用指定 file/pos,但必须确保所有节点gtid_executed集合能被新主库覆盖 - 执行前先
STOP SLAVE,执行后START SLAVE,再立刻SHOW SLAVE STATUS\G看Seconds_Behind_Master是否归零、Slave_IO_Running和Slave_SQL_Running是否都为Yes
验证切换后的一致性与可用性
切完就改 DNS 或 VIP?太早。真正危险的是“看起来在跑,其实数据不对”。比如主库宕机前最后一秒的事务没传过来,但从库没报错,你却以为切成功了。
必须做两件事:一是对比关键表的行数和校验和(如用 pt-table-checksum),二是模拟真实业务写入并查回——不能只查 SELECT,要 INSERT + SELECT + DELETE 全链路走一遍。
- 检查
SHOW PROCESSLIST中是否有长时间运行的Connect或Query状态线程,可能是旧连接还在往老主库发请求 - 注意时区、SQL mode、字符集是否一致,这些差异不会导致复制中断,但会让同一条 SQL 在新主库产生不同结果
- 如果用了代理(如 ProxySQL、MaxScale),记得同步更新其后端配置,否则流量仍可能打到已下线的旧主库










