MySQL 8.0 主从复制搭建与故障切换演练

主从复制是 MySQL 高可用的基础:主库承接写入,从库分担读流量并在主库故障时顶上去。本文以 GTID 复制为主线,完整走一遍 MySQL 8.0 的搭建、检查和切换流程,并把计划外切换时的风险点单独拎出来讲——切换本身不难,难的是切之前确认什么、切之后旧主怎么处理。

适用环境:MySQL 8.0.28 及以上,两台主机 db-01(192.0.2.21,主)与 db-02(192.0.2.22,从),操作系统为 Linux。文中口令均以占位符表示,实际使用请替换为强口令并通过安全通道下发。

前置条件

  • 两台主机已安装同版本 MySQL,数据目录、端口(3306)规划一致;
  • 主机间网络互通,防火墙放行 3306,时间已同步(chrony 或 ntpd);
  • 主库为新建实例或已有数据但可短暂停写做全量导出;
  • 两台主机的 server_id 全局唯一,且规划好后续扩容编号。

第一步:配置主库参数

在 db-01 的 /etc/my.cnf 的 [mysqld] 段加入以下参数后重启 MySQL:

[mysqld]
server_id = 101
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

说明:GTID 模式开启后,每个事务有全局唯一编号,从库换源、切换后重新挂接都靠它自动定位,是 8.0 时代的默认选择,不要再用文件加位置的老方式。sync_binlog=1 与 innodb_flush_log_at_trx_commit=1 是数据安全的双保险,性能敏感场景放宽前先想清楚丢数据的代价。binlog 默认保留 7 天,磁盘紧张时缩短,但要覆盖最长的从库停机维护窗口。

第二步:创建复制账号

CREATE USER 'repl'@'192.0.2.%' IDENTIFIED WITH caching_sha2_password BY 'CHANGE_ME_STRONG';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.0.2.%';
FLUSH PRIVILEGES;

授权网段只写从库所在网段,不要图省事写 %。caching_sha2_password 是 8.0 默认认证插件,复制连接走它需要 TLS 或允许 RSA 公钥交换;图省事也可以对复制账号单独用 mysql_native_password,但更推荐给复制链路配置 SSL。

第三步:初始化从库并启动复制

从库 my.cnf 同样开启 GTID,server_id 设为 102,并建议加 read_only 与 super_read_only 防误写。有存量数据时,先在主库做一致性备份再导入从库:

# 主库导出(含 GTID 信息与源信息)
mysqldump -uroot -p --single-transaction --set-gtid-purged=ON \
  --source-data=2 --all-databases > /backup/full.sql

# 从库导入后建立复制
mysql -uroot -p < /backup/full.sql

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='192.0.2.21',
  SOURCE_PORT=3306,
  SOURCE_USER='repl',
  SOURCE_PASSWORD='CHANGE_ME_STRONG',
  SOURCE_AUTO_POSITION=1,
  GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;

SOURCE_AUTO_POSITION=1 表示按 GTID 自动找同步点,这是 GTID 模式下最关键的一行;漏了它或从库导入的备份不含 GTID 集合,复制会直接报错。新库无数据时可跳过导出导入,直接 CHANGE 后启动即可全量拉取(仅适合数据量小的场景)。

第四步:读懂复制状态

SHOW REPLICA STATUS\G

重点看这几个字段:

  • Replica_IO_Running / Replica_SQL_Running:都应为 Yes,一个拉日志、一个回放日志,任一为 No 先看同段的 Last_IO_Error / Last_SQL_Error。
  • Seconds_Behind_Source:延迟秒数。只能作参考:IO 线程断开时它会显示 NULL,大事务回放时数值会跳变,不能单独作为告警依据。
  • Retrieved_Gtid_Set / Executed_Gtid_Set:已接收与已执行的 GTID 集合,两者差集就是还没回放的事务,这才是判断追平的硬指标。
  • Auto_Position:应为 1,确认走的是 GTID 自动定位。

延迟监控建议双管齐下:一是定期比对主从 Executed_Gtid_Set 的差距,二是在主库写心跳表(每秒更新一行带时间戳的记录),从库读出时间差即为真实延迟,比 Seconds_Behind_Source 稳定。业务侧的延迟容忍度要在切换标准里写死,例如「延迟超过 30 秒的从库不参与故障切换」。

第五步:计划内切换的标准动作

计划内切换(维护、升级)按以下顺序执行,前后约几分钟:

  1. 通知业务方进入维护窗口,应用侧先停写或切到只读;
  2. 在主库执行 FLUSH TABLES WITH READ LOCK 或直接停止应用写入,确认 binlog 不再增长;
  3. 在从库反复查 Executed_Gtid_Set,直到与主库的 gtid_executed 完全一致,即确认追平;
  4. 从库执行 STOP REPLICA,并 RESET REPLICA ALL 清除旧复制关系,随后 SET GLOBAL read_only=OFF、super_read_only=OFF 提升为可写;
  5. 原主库改为指向新主库的从库:CHANGE REPLICATION SOURCE TO ... SOURCE_HOST='192.0.2.22' ... 后 START REPLICA,并设为只读;
  6. 应用连接指向新主库(改 VIP 漂移、DNS 或连接串,见第七步),恢复写入;
  7. 观察 10–15 分钟:错误日志、复制状态、业务关键接口成功率。

第六步:计划外故障切换的风险

主库宕机时的切换与计划内完全不同,核心风险是数据不一致,动作顺序要反着来,先确认再提升:

  • 先确认主库真的不可用:区分进程崩溃、主机宕机与网络分区。网络分区下原主库可能还在接写入,冒然提升从库会造成双写脑裂,两边数据无法自动合并。
  • 比对多个从库再选新主:有多个从库时,选 Executed_Gtid_Set 最全的那台提升;其余从库切换后会自动按 GTID 补齐或丢弃多余事务,但提升的那台若不是数据最全的,已确认的事务可能丢失。
  • 旧主处理:旧主修复后绝不能直接对外提供写服务。它上面可能有未同步到新主的事务,应先只读启动,导出差异数据与新主核对,业务确认后再决定是丢弃还是回补;确认完毕后将其作为从库重新挂到新主下(GTID 会自动处理冲突事务,多余的本地事务需用空事务跳过或重建该从库)。
  • 全过程留痕:故障时间点、GTID 比对结果、提升命令、谁在什么时间执行的决策,都要记进故障单,复盘时这是唯一依据。

第七步:应用连接指向

切换做得再标准,应用还连着旧库就等于没切。三种常见做法:

  • VIP 漂移:keepalived 管理一个虚拟 IP,切换时把 VIP 绑到新主。动作快、对应用透明,但要注意 ARP 缓存与长连接不会自动断开,连接池要配合连接有效性检测。
  • DNS / 配置中心:改解析或配置项,简单但生效慢,适合计划内切换。
  • 中间件:ProxySQL、MySQL Router 等按健康状态自动路由,是规模化后的正解,但引入了新的组件与故障域。

无论哪种,都要在切换脚本里把「旧主设为只读」做成强制步骤,防止应用因连接缓存继续往旧主写。

演练记录清单

  • 演练日期、参与人、演练类型(计划内 / 模拟宕机);
  • 切换前主从 GTID 集合与延迟基线;
  • 每个步骤的执行时间、命令与结果截图或日志片段;
  • 切换总时长、业务中断时长、数据比对结果(行数校验、抽样校验和);
  • 发现的问题与配置修正项,指定责任人与完成期限;
  • 回切验证结果:演练环境应完整走一遍切回流程。

验证

-- 主库写入测试
CREATE DATABASE IF NOT EXISTS drill;
CREATE TABLE drill.t (id INT PRIMARY KEY AUTO_INCREMENT, note VARCHAR(64));
INSERT INTO drill.t (note) VALUES ('replication-check');

-- 从库应能立即查到,且 SHOW REPLICA STATUS 两个线程均为 Yes
SELECT * FROM drill.t;
SHOW REPLICA STATUS\G

常见问题

  • 报错 1236(binlog 被清理):从库要的 binlog 已在主库过期删除。先确认 binlog 过期时间设置是否过短;已发生时只能重做从库(重新全量备份导入),所以从库长时间停机前务必评估主库 binlog 保留期。
  • 报错 1032 / 1062(回放找不到行或主键冲突):通常是从库被误写产生了脏数据,或主从数据本来就不一致。用 pt-table-checksum 校验、pt-table-sync 修复,根治办法是给从库开 super_read_only 并回收从库写权限。
  • 延迟突然变大且不回落:先看是大事务(一次更新百万行)在单线程回放堵住,还是从库硬件瓶颈。8.0 可开启多线程回放(replica_parallel_workers)并配合 replica_preserve_commit_order 缓解,但业务侧拆分大事务才是根本。
  • caching_sha2_password 导致复制连不上:报错含 "Authentication requires secure connection"。给 CHANGE 命令加 GET_SOURCE_PUBLIC_KEY=1,或为复制链路开启 REQUIRE SSL,二选一即可。

小结:GTID 让 MySQL 复制的搭建和重新挂接都简化了很多,但切换的纪律性没有变:计划内切换赢在顺序,计划外切换赢在确认。把演练做成每季度一次的固定动作,并留下清单记录,真出故障时才不会在凌晨三点临场发明流程。

发表回复

后才能评论