上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器搭建MySQL高可用架构,配置主从复制与备份?

发布人:Minchunlin 发布时间:2026-10-07 15:06 阅读量:6

在香港服务器上搭建 MySQL 高可用,建议至少采用“两台数据库服务器 + 独立备份存储”的结构:一台作为主库(Source),另一台作为副本库(Replica),通过 GTID 异步复制保持数据同步,再使用独立备份节点保存全量备份和 Binlog。主库发生故障时,将副本库提升为新主库,并切换应用连接入口。

一、确定架构、RPO 与 RTO|节点规划配图

需要明确的是,主从复制本身只能减少单点故障和恢复时间,并不等于自动故障转移。下面以 Ubuntu 22.04/24.04、MySQL 8.0.23 及以上版本或 MySQL 8.4 为例,搭建一套可验证、可回滚的双节点方案。该方案默认采用人工确认后切换,适合中小型业务;如果要求无人值守自动切换,应进一步使用三节点 InnoDB Cluster、仲裁节点和故障隔离机制。

A5数据提供香港物理服务器租用资源,覆盖Xeon Gold、AMD EPYC等平台,并配备SSD或NVMe存储、不同内存容量及CN2和国际带宽方案,可承载MySQL主库、副本库、备份与业务接口等多节点部署。针对数据规模较大或备份归档需求较强的场景,香港存储系列及日本等地区的服务器资源,也可为数据库备份、Binlog留存和跨地区业务架构提供硬件与网络基础。

一、确定架构、RPO 与 RTO

1. 节点规划

以下地址均为示例私网地址,实际部署时替换为香港服务器提供商分配的内网地址。

节点示例地址角色主要职责
db-primary10.0.10.11主库接收业务写入,产生 Binlog
db-replica10.0.10.12副本库接收复制,故障时提升为主库
backup-node10.0.20.20备份节点保存全量备份、Binlog 和校验文件
app 或数据库入口业务网络地址应用连接端连接当前可写数据库

应用不要把数据库 IP 直接写死在多个业务服务中。可以使用内部 DNS 名称,例如 mysql-write.internal,也可以由应用配置中心维护当前主库地址。发生切换时,只修改一个连接入口,避免逐台修改应用配置。

如果使用云平台浮动 IP 或虚拟 IP,需要先确认香港服务器网络是否支持地址漂移、ARP 通告或负载均衡后端切换。不能仅因为系统中配置了一个 VIP,就认为它具备故障转移能力。

2. 可接受的数据丢失范围

本教程使用异步复制。主库已经提交但尚未传输到副本库的事务,在主库和本地 Binlog 同时损坏时可能丢失,因此 RPO 不是绝对为零。

目标双节点异步复制的典型表现需要重点配置的部分
RPO通常接近复制延迟,但网络中断期间可能扩大监控复制延迟、保留 Binlog
RTO取决于故障确认、提升副本和切换应用入口的时间切换流程、DNS 或代理入口
数据恢复依赖全量备份和 Binlog独立备份、定期恢复演练
自动切换默认不提供需要仲裁、故障隔离和自动化工具

如果业务不能接受异步复制带来的数据缺口,可以评估半同步复制或三节点组复制。但半同步会增加提交延迟,也不能替代备份和恢复演练。

3. 容量和备份空间估算

备份空间不能只按数据库当前大小计算,还要加入 Binlog、临时文件、压缩失败时的双份文件和保留周期。

例如:

  • 数据库全量大小约 200 GB;
  • 每天新增 Binlog 约 10 GB;
  • Binlog 保留 7 天;
  • 预留 30% 操作空间。

则基础空间约为:

200 GB + 10 GB × 7 = 270 GB
270 GB × 1.3 = 351 GB

这里使用十进制 GB 进行估算。实际还应为每周备份、多个恢复点和校验文件留出空间。

二、准备服务器和软件环境

1. 核对系统、MySQL 和磁盘

以下命令适用于 Debian/Ubuntu 系统。若使用 Rocky Linux、AlmaLinux 或其他发行版,服务名、配置路径和防火墙命令可能不同,应先核对后再执行。

cat /etc/os-release
uname -a
mysql --version
sudo systemctl status mysql --no-pager
lsblk
df -h
timedatectl

建议满足以下条件:

  • 两台服务器使用相同的 CPU 架构和相同的 MySQL 大版本;
  • MySQL 至少使用 8.0.23,以便使用 SOURCE 和 REPLICA 新语法;
  • 两台服务器的 server_id 必须不同;
  • 两台服务器都配置时间同步;
  • 数据盘、日志盘和备份盘有足够余量;
  • 主库、副本库和备份存储不能完全依赖同一块磁盘;
  • 复制端口只允许指定的数据库节点访问。

检查 MySQL 版本是否满足要求:

mysql -NBe "SELECT VERSION(), @@version_comment;"

示例输出:

8.0.36-0ubuntu0.22.04.1    (Ubuntu)

如果版本低于 8.0.23,应使用对应版本的旧语法,或者先规划升级。不要在生产环境中混用不同版本的复制语法。

2. 配置主机名和时间

分别在两台服务器上设置唯一主机名:

sudo hostnamectl set-hostname db-primary

副本服务器执行:

sudo hostnamectl set-hostname db-replica

确认时间同步状态:

timedatectl status

如果系统未启用时间同步,可以执行:

sudo timedatectl set-ntp true

时间偏差不会直接改变 GTID,但会影响日志分析、备份命名、故障判断和恢复到指定时间点。

3. 配置最小网络访问范围

在主库上,只允许副本库访问 3306 端口:

sudo ufw allow from 10.0.10.12 to any port 3306 proto tcp
sudo ufw reload
sudo ufw status numbered

在副本库上,允许主库访问 3306 端口:

sudo ufw allow from 10.0.10.11 to any port 3306 proto tcp
sudo ufw reload
sudo ufw status numbered

如果应用也直接连接数据库,还需要额外允许应用网段访问 3306。不要直接执行以下宽泛规则:

sudo ufw allow 3306/tcp

防火墙调整会改变服务可达范围。执行前应记录现有规则,确认 SSH 管理端口仍然可用。回滚时删除新增规则,并再次检查远程管理连接。

三、配置 MySQL 主库和副本库

1. 创建独立配置文件

Ubuntu 的 MySQL 配置通常位于 /etc/mysql/mysql.conf.d/,但不同安装方式可能不同。先确认配置加载路径:

mysqld --verbose --help 2>/dev/null | grep -A2 "Default options"

确认目录被主配置文件包含后,使用 sudoedit 创建配置文件。不要直接覆盖原有配置文件。

主库创建:

sudoedit /etc/mysql/mysql.conf.d/60-ha.cnf

写入以下内容:

[mysqld]
server-id=101
bind-address=10.0.10.11
skip_name_resolve=ON

log_bin=/var/log/mysql/mysql-bin
binlog_format=ROW
binlog_row_image=FULL
gtid_mode=ON
enforce_gtid_consistency=ON
log_replica_updates=ON

sync_binlog=1
innodb_flush_log_at_trx_commit=1
binlog_expire_logs_seconds=604800

副本库创建相同文件,但使用不同的 server-id 和地址:

sudoedit /etc/mysql/mysql.conf.d/60-ha.cnf
[mysqld]
server-id=102
bind-address=10.0.10.12
skip_name_resolve=ON

log_bin=/var/log/mysql/mysql-bin
relay_log=/var/log/mysql/mysql-relay
binlog_format=ROW
binlog_row_image=FULL
gtid_mode=ON
enforce_gtid_consistency=ON
log_replica_updates=ON
relay_log_recovery=ON

read_only=ON
super_read_only=ON
sync_binlog=1
innodb_flush_log_at_trx_commit=1
binlog_expire_logs_seconds=604800

其中:

  • server-id 用于区分复制节点,不能重复;
  • binlog_format=ROW 可以降低部分非确定性语句导致的数据差异;
  • gtid_mode=ON 让复制基于事务标识,而不是手工维护文件名和位置;
  • log_replica_updates=ON 便于副本库将来继续作为新主库传播事务;
  • read_only 和 super_read_only 用于防止误写副本库;
  • sync_binlog=1 和 innodb_flush_log_at_trx_commit=1 更重视已提交事务的持久性,但会增加磁盘 I/O;
  • binlog_expire_logs_seconds 不能小于副本可能中断的时间和备份恢复窗口。

执行配置检查:

sudo mysqld --validate-config

如果系统或版本不支持该参数,可以直接检查服务日志:

sudo systemctl restart mysql
sudo systemctl status mysql --no-pager
sudo journalctl -u mysql -n 80 --no-pager

重启会中断当前数据库连接。生产环境应在维护窗口执行,并提前确认已有备份。若重启失败,恢复刚才保存的配置文件,再执行:

sudo systemctl daemon-reload
sudo systemctl restart mysql

检查关键参数:

mysql -e "
SELECT
  @@server_id AS server_id,
  @@global.gtid_mode AS gtid_mode,
  @@global.enforce_gtid_consistency AS enforce_gtid_consistency,
  @@global.log_bin AS log_bin,
  @@global.binlog_format AS binlog_format,
  @@global.read_only AS read_only,
  @@global.super_read_only AS super_read_only;
"

主库预期 server_id=101、log_bin=ON、read_only=0。副本库预期 server_id=102、log_bin=ON、read_only=1、super_read_only=1。

2. 创建复制账号

在主库创建只用于复制的账号。由于配置了 skip_name_resolve=ON,账号主机部分应使用 IP 地址,不能依赖主机名解析。

CREATE USER 'repl'@'10.0.10.12'
  IDENTIFIED BY '替换为长度足够的随机密码'
  REQUIRE SSL;

GRANT REPLICATION SLAVE, REPLICATION CLIENT
  ON *.*
  TO 'repl'@'10.0.10.12';

SHOW GRANTS FOR 'repl'@'10.0.10.12';

MySQL 8.0 中 REPLICATION SLAVE 是现有权限名称,虽然新命令使用了 SOURCE 和 REPLICA 术语。

复制连接建议启用 TLS。先检查主库是否存在可用的 CA 和服务器证书:

SHOW VARIABLES LIKE 'ssl%';
SHOW VARIABLES LIKE 'tls_version';

如果没有可验证的 CA 文件,不要为了快速连通而删除 REQUIRE SSL。应先部署由内部 CA 或可信证书链签发的数据库证书,并确认副本库可以读取 CA 文件。

3. 检查主库表引擎和数据状态

mysqldump --single-transaction 对 InnoDB 表可以提供一致性快照,但对 MyISAM 等非事务表不具备相同保证。先检查非 InnoDB 表:

SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'sys')
  AND ENGINE IS NOT NULL
  AND ENGINE <> 'InnoDB';

如果返回业务表,应先评估停写窗口、表转换或其他物理备份方式。不要在未确认数据一致性的情况下直接把逻辑备份作为副本初始数据。

四、初始化副本库并建立 GTID 复制

1. 在主库生成初始备份

以下命令适合中小规模数据库,备份期间不需要长时间锁住 InnoDB 表。命令应在主库或备份服务器执行,并将文件保存到独立磁盘。

set -o pipefail
umask 077

backup_file="/backup/mysql/full-$(date +%F-%H%M%S).sql.gz"

mysqldump \
  --single-transaction \
  --quick \
  --routines \
  --events \
  --triggers \
  --hex-blob \
  --all-databases \
  --set-gtid-purged=ON \
  | gzip -c > "${backup_file}"

gzip -t "${backup_file}"
sha256sum "${backup_file}" > "${backup_file}.sha256"

--all-databases 会包含系统库和账号信息。该操作只应恢复到一台新初始化或已经确认为空的副本实例。不要把它直接导入正在承载业务数据的服务器。

如果数据库较大,逻辑导出会占用 CPU、磁盘和网络资源,也可能产生较长恢复时间。可以使用与 MySQL 大版本匹配的 Percona XtraBackup 等物理备份工具,但必须先核对工具版本与 MySQL 版本兼容性,不能直接套用不同版本的参数。

检查备份文件:

ls -lh "${backup_file}" "${backup_file}.sha256"
sha256sum -c "${backup_file}.sha256"

预期结果:

/backup/mysql/full-2025-01-15-020000.sql.gz: OK

以上日期和文件名仅为示例,不代表实际执行记录。

2. 将备份恢复到副本库

副本库必须是空实例或专门用于重新初始化的实例。恢复前先确认目标地址和端口,避免误操作生产主库:

mysql -NBe "SELECT @@hostname, @@port, @@datadir;"

将备份文件复制到副本库后恢复:

gzip -dc /backup/mysql/full-2025-01-15-020000.sql.gz \
  | mysql --host=127.0.0.1 --port=3306

导入期间会产生大量写入,并可能覆盖同名数据库对象。执行前必须确认:

  • 目标确实是副本库;
  • 目标数据已备份或确认可以清空;
  • 应用没有连接该实例;
  • 有足够的磁盘空间;
  • 恢复失败时可以重新初始化,而不是在半恢复状态上继续操作。

恢复完成后检查 GTID:

SELECT
  @@GLOBAL.gtid_mode,
  @@GLOBAL.gtid_purged,
  @@GLOBAL.gtid_executed;

如果恢复过程提示 GTID_PURGED 冲突,说明目标实例已经执行过其他 GTID 事务。不要直接执行 RESET MASTER 或清空 GTID 来绕过错误,这可能掩盖数据分叉。应停用目标实例,保存日志和现有数据后重新初始化。

3. 在副本库配置复制源

先确认副本库仍然是只读状态:

SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;

配置复制连接。下面的 CA 路径必须替换为副本服务器上真实存在、权限正确的文件:

四、初始化副本库并建立 GTID 复制|在副本库配置复制源配图

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='10.0.10.11',
  SOURCE_PORT=3306,
  SOURCE_USER='repl',
  SOURCE_PASSWORD='替换为复制账号密码',
  SOURCE_AUTO_POSITION=1,
  SOURCE_SSL=1,
  SOURCE_SSL_CA='/etc/mysql/ssl/ca.pem',
  SOURCE_SSL_VERIFY_SERVER_CERT=1;

START REPLICA;

如果复制账号使用了 REQUIRE SSL,但副本库没有正确配置 CA,复制线程会无法建立连接。此时不要删除 SSL 要求,应检查:

sudo ls -l /etc/mysql/ssl/ca.pem
sudo journalctl -u mysql -n 100 --no-pager

查看复制状态:

SHOW REPLICA STATUS\G

正常状态通常包含:

Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Auto_Position: 1
Last_IO_Error:
Last_SQL_Error:

Seconds_Behind_Source 为 NULL 不一定表示故障。复制线程停止、尚未连接、SQL 线程报错时可能为 NULL,应结合 Replica_IO_Running、Replica_SQL_Running 和错误字段判断。

五、每一步的验证与监控

1. 验证复制链路

在主库执行:

SHOW BINARY LOG STATUS;

在副本库执行:

SHOW REPLICA STATUS\G

重点关注以下字段:

检查项正常表现异常含义
Replica_IO_RunningYes网络、账号、TLS 或主库 Binlog 问题
Replica_SQL_RunningYesSQL 应用线程正常
Seconds_Behind_Source接近 0 或业务可接受范围复制延迟
Last_IO_Error空I/O 线程无错误
Last_SQL_Error空SQL 线程无错误
Retrieved_Gtid_Set持续增长已从主库获取事务
Executed_Gtid_Set接近 Retrieved_Gtid_Set副本已应用事务

可以用以下命令快速查看:

mysql -e "
SHOW REPLICA STATUS\G
" | egrep \
"Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_IO_Error|Last_SQL_Error|Retrieved_Gtid_Set|Executed_Gtid_Set"

2. 用测试事务验证复制

在主库创建专用测试库和表:

CREATE DATABASE IF NOT EXISTS ha_probe;
CREATE TABLE IF NOT EXISTS ha_probe.replication_test (
  id BIGINT PRIMARY KEY,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  note VARCHAR(100) NOT NULL
) ENGINE=InnoDB;

INSERT INTO ha_probe.replication_test(id, note)
VALUES (1, 'replication-check')
ON DUPLICATE KEY UPDATE note = VALUES(note);

在副本库查询:

SELECT * FROM ha_probe.replication_test WHERE id = 1;

如果副本库能够查询到记录,说明基本复制链路可用。测试完成后,建议保留这张小表作为健康检查对象,或者由监控系统写入唯一测试 ID。不要用业务表做随意插入和删除测试。

3. 验证副本库防误写

在副本库执行:

SELECT @@GLOBAL.read_only, @@GLOBAL.super_read_only;

预期结果为:

1    1

使用普通业务账号执行写入时,应收到只读相关错误。管理员账号可能绕过部分限制,因此不能只用管理员账号验证防误写效果。

应用读取副本库时还要考虑复制延迟。对于“写入后立即读取”的请求,应继续读取主库,或由应用使用会话级路由策略。不能因为副本库查询成功,就默认它已经包含刚刚提交的所有事务。

六、备份、Binlog 和恢复策略

1. 全量备份

可以在副本库执行全量逻辑备份,减少对主库业务的影响,但必须确认副本库没有延迟或数据错误。备份前检查:

SHOW REPLICA STATUS\G

要求 I/O 线程和 SQL 线程正常,延迟处于业务允许范围内。

示例备份脚本:

#!/usr/bin/env bash
set -euo pipefail
umask 077

backup_dir="/backup/mysql"
timestamp="$(date +%F-%H%M%S)"
tmp_file="${backup_dir}/full-${timestamp}.sql.gz.tmp"
final_file="${backup_dir}/full-${timestamp}.sql.gz"

mkdir -p "${backup_dir}"

mysqldump \
  --login-path=backup \
  --single-transaction \
  --quick \
  --routines \
  --events \
  --triggers \
  --hex-blob \
  --all-databases \
  --set-gtid-purged=ON \
  | gzip -c > "${tmp_file}"

gzip -t "${tmp_file}"
mv "${tmp_file}" "${final_file}"
sha256sum "${final_file}" > "${final_file}.sha256"

使用 mysql_config_editor 配置登录信息,避免把密码直接写在脚本参数中:

mysql_config_editor set \
  --login-path=backup \
  --host=127.0.0.1 \
  --user=backup \
  --password

执行后检查权限:

ls -l ~/.mylogin.cnf

备份文件应使用严格权限保存,并同步到与两台数据库不同故障域的加密对象存储或备份服务器。只保留在同一台香港服务器上的备份,无法应对磁盘损坏、误删、账户被入侵或整台主机不可用。

2. 归档 Binlog

全量备份只能恢复到备份时刻。要实现时间点恢复,还需要持续保存 Binlog。

可以使用 mysqlbinlog 从主库持续拉取原始 Binlog:

mysqlbinlog \
  --read-from-remote-server \
  --raw \
  --stop-never \
  --host=10.0.10.11 \
  --port=3306 \
  --login-path=binlog \
  --result-file=/backup/mysql/binlog/

该进程应交由 systemd、Supervisor 或其他进程管理器维护,并监控退出状态。--result-file 指向的目录需要有足够空间。

复制账号和 Binlog 归档账号可以分开。归档账号至少需要满足对应版本 mysqlbinlog 的读取权限,建议单独创建并限制来源地址。不要直接复制正在写入的 Binlog 文件,也不要在未确认归档成功前手工执行 Binlog 清理。

Binlog 保留策略应同时满足:

  • 副本库最长可能中断时间;
  • 全量备份周期;
  • 需要支持的时间点恢复窗口;
  • 远端归档任务的最大延迟。

3. 恢复备份并执行时间点恢复

恢复演练必须在隔离实例中进行,不要直接在生产主库上执行。基本流程为:

六、备份、Binlog 和恢复策略|恢复备份并执行时间点恢复配图

  1. 在临时 MySQL 实例恢复最近一次全量备份;
  2. 校验数据库、表、账号和字符集;
  3. 按时间顺序应用备份之后的 Binlog;
  4. 在业务验证通过后,决定是否提升为生产实例。

示例命令:

gzip -dc /backup/mysql/full-2025-01-15-020000.sql.gz \
  | mysql --host=127.0.0.1 --port=3307

应用 Binlog 时,先在隔离实例中确认文件范围和目标时间:

mysqlbinlog \
  --start-datetime="2025-01-15 02:00:00" \
  --stop-datetime="2025-01-15 08:30:00" \
  /backup/mysql/binlog/mysql-bin.000123 \
  /backup/mysql/binlog/mysql-bin.000124 \
  | mysql --host=127.0.0.1 --port=3307

时间点恢复会执行期间的全部事务。若需要恢复到误操作前一秒,应根据 Binlog 事件精确确定停止时间,不要只依靠文件名猜测。恢复完成后检查关键业务表的行数、最大 ID、订单状态和应用连接测试。

七、故障切换操作

1. 切换前的安全条件

只有在确认主库不可恢复、或计划内维护确实需要切换时,才执行提升副本操作。最重要的风险是“双主写入”,也就是旧主库尚未隔离,新副本已经开始接受写入。

七、故障切换操作|切换前的安全条件配图

切换前确认:

  • 旧主库已经停止 MySQL,或已通过云平台电源、网络 ACL、交换机规则完成隔离;
  • 副本库的 Replica_IO_Running 和 Replica_SQL_Running 均为 Yes;
  • 副本库没有 Last_SQL_Error;
  • 复制延迟在可接受范围;
  • 已记录当前 Retrieved_Gtid_Set 和 Executed_Gtid_Set;
  • 应用写入已经暂停,或已经准备好短暂维护窗口;
  • 已记录当前连接入口、DNS、代理或应用配置。

如果旧主库完全失联,无法确认其状态,应优先通过服务器控制台关机或隔离网络。仅修改应用连接地址并不能阻止旧主库继续写入。

2. 将副本库提升为主库

在副本库执行:

STOP REPLICA;
SHOW REPLICA STATUS\G;

确认已经停止后,记录复制状态,再清理复制关系:

RESET REPLICA ALL;

RESET REPLICA ALL 会删除副本连接参数和复制元数据,但不会自动删除业务表。执行前必须保存 SHOW REPLICA STATUS\G 输出,因为回滚或排查需要这些信息。

随后关闭只读:

SET GLOBAL super_read_only = OFF;
SET GLOBAL read_only = OFF;

确认角色状态:

SELECT
  @@hostname AS hostname,
  @@GLOBAL.read_only AS read_only,
  @@GLOBAL.super_read_only AS super_read_only,
  @@GLOBAL.gtid_executed AS gtid_executed;

预期 read_only=0、super_read_only=0。如果配置文件中仍然写有 read_only=ON,重启后会恢复只读,应同步修改角色配置文件,或者使用角色配置管理。

3. 切换应用连接入口

根据实际接入方式执行其中一种:

  • 修改内部 DNS mysql-write.internal 的目标地址;
  • 修改应用配置中心中的数据库写入地址;
  • 将代理或负载均衡的写入后端从旧主库切到新主库;
  • 在应用层启用新主库连接池并清理旧连接。

DNS 切换受 TTL、客户端缓存和连接池影响,不能把 TTL 数值直接等同于实际恢复时间。应用应设置合理的连接超时、重试次数和连接池失效机制。

切换后执行一个无副作用的连接检查:

mysql --host=mysql-write.internal -e "
SELECT
  @@hostname AS hostname,
  @@GLOBAL.read_only AS read_only,
  @@GLOBAL.super_read_only AS super_read_only,
  @@GLOBAL.gtid_executed AS gtid_executed;
"

确认应用可以连接新主库后,再恢复写入流量。

八、常见异常处理

1. I/O 线程为 No

查看错误:

SHOW REPLICA STATUS\G

重点查看 Last_IO_Error。常见原因包括:

  • 主库 3306 未放行;
  • 复制账号的来源 IP 不匹配;
  • 密码错误;
  • CA 路径错误或证书校验失败;
  • 主库地址或端口错误;
  • 主库 Binlog 已被清理。

不要直接删除复制账号或关闭 SSL。先使用网络和账号检查命令确认问题范围:

nc -vz 10.0.10.11 3306

2. SQL 线程为 No

常见原因是主库与副本库存在数据差异,例如重复主键、表不存在、字段定义不一致或人工在副本库写入过数据。

处理顺序应为:

  1. 保存 SHOW REPLICA STATUS\G 和错误日志;
  2. 记录 Last_SQL_Error;
  3. 暂停业务对副本库的读写;
  4. 判断是否能够修复具体数据;
  5. 无法确认一致性时,从最新全量备份重新初始化副本。

不建议使用跳过事务的方式快速恢复:

SET GLOBAL sql_slave_skip_counter = 1;

该旧方法可能让复制线程继续运行,但会留下无法察觉的数据缺口。即使临时跳过,也必须进行全量一致性校验和后续重建。

3. GTID 已被主库清理

如果主库的 Binlog 过期,而副本库仍需要这些事务,可能出现类似“purged required GTIDs”的错误。正确处理方式通常是:

  1. 停止当前复制;
  2. 找到包含完整 GTID 范围的远端 Binlog 归档;
  3. 如果归档不完整,从全量备份重新初始化;
  4. 重新执行 CHANGE REPLICATION SOURCE TO ... SOURCE_AUTO_POSITION=1;
  5. 再次观察复制状态。

不要执行 RESET MASTER 来掩盖 GTID 缺失。该操作会改变实例的 Binlog 和 GTID 状态,可能让后续恢复更加困难。

4. 复制延迟持续增大

先判断是 I/O 延迟还是 SQL 应用延迟:

  • Retrieved_Gtid_Set 不增长:网络、主库发送或复制连接异常;
  • Retrieved_Gtid_Set 增长但 Executed_Gtid_Set 落后:副本磁盘、CPU、锁等待或并行应用能力不足;
  • Seconds_Behind_Source 为 NULL:可能是线程停止,不能简单当作零延迟。

同时检查:

iostat -xz 1 5
vmstat 1 5
df -h

复制延迟期间不要贸然提升副本为主库。若必须切换,应明确可能损失的事务范围,并先保留主库和副本状态信息。

5. 备份文件校验失败

如果 gzip -t 或 SHA-256 校验失败:

  • 不要将该文件用于生产恢复;
  • 检查备份任务是否被磁盘写满、中断或提前终止;
  • 检查临时文件是否被错误改名;
  • 从远端备份重新下载或重新生成;
  • 检查备份节点的磁盘和对象存储完整性。

备份脚本应先写入 .tmp 文件,校验成功后再使用 mv 改为正式文件名,避免把半成品误认为可用备份。

九、上线验收与回滚检查项

1. 上线验收

部署完成后,逐项记录结果:

  • [ ] 两台服务器 MySQL 大版本一致,server_id 唯一;
  • [ ] 主库已启用 Binlog、GTID 和 ROW 格式;
  • [ ] 副本库已启用 read_only 与 super_read_only;
  • [ ] 复制账号仅允许副本地址连接,并使用 TLS;
  • [ ] Replica_IO_Running=Yes;
  • [ ] Replica_SQL_Running=Yes;
  • [ ] Last_IO_Error 和 Last_SQL_Error 为空;
  • [ ] 主库测试事务可以在副本库查询到;
  • [ ] 副本库普通业务账号无法写入;
  • [ ] 全量备份能够生成并通过 gzip -t;
  • [ ] 备份文件已经复制到独立故障域;
  • [ ] Binlog 归档任务正在运行;
  • [ ] 已在隔离实例完成至少一次备份恢复;
  • [ ] 应用使用统一的写入连接入口;
  • [ ] 已演练副本提升和应用切换;
  • [ ] 已记录切换后的 RPO、RTO 和实际操作时间。

2. 配置变更回滚

如果只是配置文件变更后服务启动失败:

  1. 停止继续修改;
  2. 恢复变更前的 60-ha.cnf;
  3. 执行 mysqld --validate-config;
  4. 重启 MySQL;
  5. 检查 Binlog、GTID 和业务连接。

如果复制初始化失败,但副本尚未承载业务写入,可以停止 MySQL,保留错误日志和当前数据目录,再重新初始化。不要在半恢复状态下反复导入备份。

如果已经完成副本提升并产生了新写入,不要简单执行“反向复制”把旧主库重新接回。正确做法是:

  1. 将旧主库隔离并保持不可写;
  2. 以新主库为数据源重新生成全量备份;
  3. 在旧主库上恢复备份;
  4. 让旧主库作为新主库的副本重新建立 GTID 复制;
  5. 完成一致性检查后,再决定是否恢复双节点结构。

这样可以避免旧主库和新主库各自产生过写入后形成数据分叉。最终验收标准不是“两个 MySQL 服务都在运行”,而是当前只有一个明确的可写角色、复制状态正常、备份可以恢复,并且故障切换和回滚路径都经过验证。