香港服务器部署业务系统,如何用主备复制与恢复目标设计高可用方案
先确定目标状态:可切换,不等于自动恢复
香港服务器部署业务系统时,主备方案的核心不是“再准备一台服务器”,而是把单点故障拆开处理:业务入口、数据库写入节点、数据库副本、切换动作和恢复验证都要有明确边界。
一个稳妥的目标状态是:正常情况下,应用只向主数据库写入,备用数据库持续复制并保持只读;主库出现故障时,先阻断旧主库继续接收写入,再提升备用库,最后将应用连接切换到新主库。没有完成“隔离旧主库”这一步,不应直接提升备用库,否则两个节点可能同时写入,形成脑裂。

下面以两台香港服务器、MySQL 8.0.23及以上、systemd管理服务为例。两台服务器分别承担:
| 角色 | 主要职责 | 正常写入状态 |
|---|---|---|
| 主库 | 接收业务写入、生成二进制日志 | 允许业务写入 |
| 备用库 | 接收复制日志、提供故障切换后的数据库服务 | 只读 |
| 应用入口 | 使用固定的数据库连接配置、受控域名或可切换地址 | 只连接当前主库 |
这个方案默认使用异步GTID复制,因此不能直接承诺零数据丢失。实际恢复点取决于主库故障时备用库已经执行到的事务;如果业务要求RPO为零,需要另外验证半同步或同步复制方案,不能用普通异步复制替代。
现状核对:先确认单点和恢复目标
1. 明确RPO和RTO
变更前先把两个指标写进变更单,而不是等故障时临时判断:
- RPO:主库故障后,业务最多允许丢失多少已经提交的数据。
- RTO:从确认故障到业务恢复写入,最多允许中断多长时间。
异步复制的实际RPO至少包含复制延迟期间尚未在备用库执行的事务。RTO则由故障发现、旧主库隔离、备用库提升、应用连接切换、连接池刷新和业务验证共同决定。
可以按下面的方式制定判断规则:
| 业务目标 | 方案判断 | 必须验证的内容 |
|---|---|---|
| 可以接受少量最近事务丢失 | 异步GTID主备可作为基础方案 | 备用库已执行GTID、复制延迟、故障切换流程 |
| 不接受已提交事务丢失 | 普通异步复制不满足要求 | 半同步或同步机制、提交确认路径和断链行为 |
| 需要尽快恢复写入 | 需要预先准备切换入口和人工操作手册 | 隔离旧主库、提升备用库、刷新应用连接池 |
| 只要求数据库可用 | 不代表业务完整可用 | 业务表、账号权限、定时任务和外部依赖是否同步 |
不要只看Seconds_Behind_Source判断数据是否完整。这个字段可能因为复制线程停止、网络中断或事务执行状态异常而显示为空,最终应结合复制线程状态、GTID集合和业务探针判断。
2. 检查版本、服务名和现有复制状态
以下命令只进行读取,不会修改业务数据。命令以MySQL服务名为mysql的Linux环境为例;如果系统实际服务名不同,应先查明,不要盲目重启。
sudo systemctl list-unit-files | grep -E '^(mysql|mysqld)(\.service)?'
sudo systemctl status mysql --no-pager
mysql --login-path=admin -NBe "
SELECT
@@version,
@@hostname,
@@datadir,
@@server_id,
@@global.gtid_mode,
@@global.enforce_gtid_consistency;
"
本方案要求:
- 两台服务器的MySQL主版本和小版本尽量一致;
server_id必须唯一;- 两台服务器的
gtid_mode已经处于ON; enforce_gtid_consistency已经处于ON;- 业务主要使用InnoDB事务表;
- 应用连接信息可以在不改动业务代码的情况下切换;
- 备用库有足够磁盘保存数据、二进制日志和中继日志。
如果现有环境的gtid_mode为OFF,不要直接把配置改成ON后重启。GTID启用需要按MySQL规定的过渡顺序逐台实施,并先在测试环境验证。现有主库已经承载业务时,应把GTID迁移单独作为一次变更。
3. 识别真正的单点
需要确认以下问题:
- 应用是否把数据库地址写死在多个配置文件中;
- 是否仍有脚本、定时任务或后台程序直接连接旧主库;
- 备用库是否被误当成读写节点使用;
- 旧主库故障后,谁有权限停止应用写入;
- 数据库之外的业务状态是否仍保存在单台服务器本地;
- 备份是否能够在独立环境完成恢复,而不只是生成了备份文件。
主备复制只能覆盖已经写入数据库并进入复制链路的数据。上传文件、异步任务、缓存内容或本地日志如果参与业务结果,也要在变更单中单独确认,否则数据库恢复成功不等于业务恢复成功。
变更准备:先留备份和回滚入口
1. 准备访问控制和维护窗口
建议为复制建立独立账号,只授予复制所需权限,并将来源限制为备用服务器地址。下面的STANDBY_IP、PRIMARY_IP和密码均为占位符,不要直接照抄到生产环境。
在主库执行前,先确认账号密码不会被写入公开脚本、工单或命令历史。生产环境还应优先为复制链路启用MySQL TLS,并使用实际证书路径。
CREATE USER 'repl_user'@'STANDBY_IP'
IDENTIFIED BY '';
GRANT REPLICATION SLAVE, REPLICATION CLIENT
ON *.* TO 'repl_user'@'STANDBY_IP';
FLUSH PRIVILEGES;
如果复制账号已经存在,先核对来源地址、认证插件和权限,不要重复创建或覆盖账号。修改账号密码会影响已经建立的复制连接,应安排在维护窗口内进行。
2. 备份数据库和配置文件
配置变更前保存当前配置。下面的路径是Debian或Ubuntu常见路径,其他发行版应先确认实际配置文件位置。
sudo cp -a \
/etc/mysql/mysql.conf.d/mysqld.cnf \
/etc/mysql/mysql.conf.d/mysqld.cnf.pre-ha.$(date +%F-%H%M%S)
如果没有经过验证的可恢复备份,应先完成备份再继续。对于主要使用InnoDB的数据库,可以在业务低峰期生成逻辑备份:
sudo install -d -m 700 /var/backups/mysql
mysqldump \
--login-path=admin \
--all-databases \
--single-transaction \
--routines \
--events \
--triggers \
--hex-blob \
--set-gtid-purged=ON \
--result-file=/var/backups/mysql/full-$(date +%F-%H%M%S).sql
--single-transaction主要保证事务表的一致性快照。如果数据库包含大量非事务表、正在进行结构变更或存在长事务,不能仅凭这个命令判断备份一致,应采用业务维护窗口或经过验证的物理备份方式。
备份完成后至少检查文件是否存在、大小是否符合预期,并在备用库或隔离环境进行一次恢复演练。备份文件应限制权限,不要让应用用户读取。
分步实施:建立主备复制链路
1. 配置主库二进制日志
以下配置以MySQL 8.0.23及以上为前提。/var/lib/mysql/binlog是示例路径,实际应确认MySQL运行用户有权创建和写入。
主库配置示例:
[mysqld]
server-id=101
log_bin=/var/lib/mysql/binlog
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
innodb_flush_log_at_trx_commit=1
sync_binlog=1
innodb_flush_log_at_trx_commit=1和sync_binlog=1侧重事务持久性,但可能增加磁盘写入压力,是否长期启用应通过业务压测确认。不要把示例中的server-id直接用于两台服务器,备用库必须使用不同值。
修改配置前确认已保存原文件和当前变量值。重启数据库会造成短暂中断,必须放在维护窗口执行:
sudo systemctl restart mysql
sudo systemctl status mysql --no-pager
如果重启失败,先查看错误日志和配置语法,不要连续重复重启。回滚时恢复变更前的配置文件,再重启服务;如果主库已经产生新的二进制日志,不能仅靠恢复配置文件回退数据状态。
2. 配置备用库
备用库必须开启二进制日志和中继日志恢复能力,以便未来提升为主库后继续向新的备用库提供复制。
[mysqld]
server-id=102
log_bin=/var/lib/mysql/binlog
relay_log=/var/lib/mysql/relay-bin
log_replica_updates=ON
relay_log_recovery=ON
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
read_only=ON
super_read_only=ON
innodb_flush_log_at_trx_commit=1
sync_binlog=1
read_only和super_read_only用于避免应用误写备用库。应用账号不应拥有高权限,不能通过管理权限绕过只读保护。复制线程仍需要能够在备用库执行复制事务。
修改备用库配置后,同样先保存原文件,再在维护窗口重启:
sudo systemctl restart mysql
sudo systemctl status mysql --no-pager
如果备用库已经承载过其他复制任务,不要直接覆盖配置。应先保存复制状态、错误日志和现有数据,再决定是修复还是重建备用库。
3. 将主库数据初始化到备用库
仅配置复制账号并不能补齐备用库已有的数据。应先把主库的一致性备份传到备用库,再恢复到一个干净、版本匹配的备用实例。
scp -p \
/var/backups/mysql/full-YYYY-MM-DD-HHMMSS.sql \
admin@STANDBY_IP:/var/backups/mysql/
以下恢复命令只适用于已经确认可以覆盖的初始化实例。若备用库已有有效数据,先停止操作并重新制定重建方案,不能直接导入覆盖。
mysql --login-path=admin \
< /var/backups/mysql/full-YYYY-MM-DD-HHMMSS.sql
恢复期间应观察错误输出和磁盘空间。恢复完成后,确认数据库版本、字符集、表数量和关键业务表记录数与主库大致一致。记录恢复使用的文件名和时间,便于故障回溯。
4. 启动GTID复制
在备用库建立复制关系。下面的命令适用于MySQL 8.0.23及以上的CHANGE REPLICATION SOURCE TO语法:
mysql --login-path=admin <<'SQL'
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='PRIMARY_IP',
SOURCE_PORT=3306,
SOURCE_USER='repl_user',
SOURCE_PASSWORD='',
SOURCE_AUTO_POSITION=1;
START REPLICA;
SQL
密码不要放进共享脚本。生产环境建议在已配置证书的前提下增加复制链路TLS参数,例如:
CHANGE REPLICATION SOURCE TO
SOURCE_SSL=1,
SOURCE_SSL_CA='/etc/mysql/ssl/ca.pem',
SOURCE_SSL_CERT='/etc/mysql/ssl/replica-client-cert.pem',
SOURCE_SSL_KEY='/etc/mysql/ssl/replica-client-key.pem';
证书路径、文件权限和主库SSL配置必须以实际环境为准。不要为了排除连接错误而随意关闭认证或使用公开可读的证书私钥。
查看复制状态:
mysql --login-path=admin -e "SHOW REPLICA STATUS\G"
重点核对:
Replica_IO_Running为Yes;Replica_SQL_Running为Yes;Last_IO_Error为空;Last_SQL_Error为空;Auto_Position为1;Retrieved_Gtid_Set持续增长;Executed_Gtid_Set能够追上主库产生的事务。
如果当前MySQL版本低于8.0.23,相关命令可能仍使用旧的CHANGE MASTER TO和SHOW SLAVE STATUS语法。不要混用两套语法,应先确认版本,再按对应版本的官方语法执行。
成功验证:先验证复制,再验证切换
1. 用GTID和复制线程判断追平状态
在主库和备用库分别执行:
SELECT
@@hostname AS host_name,
@@global.gtid_executed AS executed_gtid;
备用库的GTID集合不一定会以文本形式完全相同,但应能够覆盖主库已经提交并发送到备用库的事务。对于一次测试事务,可以在主库执行以下探针操作。该操作会创建测试对象并写入一条数据,应使用专用探针库,不要修改真实业务表。
CREATE DATABASE IF NOT EXISTS ha_probe;
CREATE TABLE IF NOT EXISTS ha_probe.replication_probe (
id BIGINT PRIMARY KEY,
checked_at TIMESTAMP NOT NULL
);
INSERT INTO ha_probe.replication_probe (id, checked_at)
VALUES (UNIX_TIMESTAMP(), CURRENT_TIMESTAMP);
记录这次写入之后主库的GTID,再在备用库等待该GTID:
SELECT WAIT_FOR_EXECUTED_GTID_SET('<主库刚刚记录的GTID>', 30);
返回0表示在等待时间内执行完成,返回1表示超时,返回NULL通常表示参数或执行状态异常。测试结束后,不要在未确认复制正常前删除探针对象;如需清理,应在维护窗口执行,并确认清理动作同样完成复制。
2. 验证备用库确实拒绝业务写入
使用业务应用账号在备用库执行一条无害的写入测试。如果返回只读错误,说明保护生效;如果业务账号能够写入,应立即停止切换准备,检查账号权限和super_read_only状态。
mysql --login-path=app_readonly -e \
"SELECT @@read_only, @@super_read_only;"
不要使用管理员账号代替应用账号做这项验证,否则结果不能代表真实业务行为。
3. 做一次受控切换演练
正式故障前应安排一次有维护窗口的切换演练,流程如下:

- 在应用入口启用维护模式或暂停写入;
- 确认主库没有新的业务写入;
- 记录主库最新GTID;
- 确认备用库已经执行到该GTID;
- 停止主库或隔离其数据库端口;
- 在备用库停止复制并提升为可写;
- 切换应用连接地址;
- 用业务探针验证登录、查询、写入和事务提交;
- 观察应用错误日志、数据库错误日志和连接池状态。
切换完成后,旧主库不能直接重新开放写入。它应先保持隔离,作为待重建的备用节点处理。
故障切换:先隔离旧主,再提升备用库
1. 提升前的判断顺序
发生主库故障时,先从低风险动作开始:
- 从应用层确认是数据库连接失败,还是应用自身异常;
- 从备用库查看复制线程和最近错误;
- 检查主库是否仍然可能接受写入;
- 确认是否已经完成网络、服务或应用层隔离;
- 评估备用库最后执行的GTID与主库最后提交状态。
如果无法证明旧主库已经停止写入,不能直接执行提升。宁可保持业务不可写,也不要在两个节点都开放写入。
2. 在备用库执行提升
确认旧主库已被停止或隔离后,在备用库执行:
STOP REPLICA;
SET GLOBAL super_read_only=OFF;
SET GLOBAL read_only=OFF;
随后检查:
SELECT
@@hostname,
@@read_only,
@@super_read_only,
@@global.gtid_executed;
确认备用库已经成为唯一可写节点后,再切换应用连接配置、受控域名或数据库入口。应用连接池可能保留旧连接,必要时应按应用自身机制重启连接池或滚动重启应用实例。
切换后不要只执行SELECT 1。至少验证:
- 一个真实业务查询;
- 一个可回滚的业务事务;
- 一个正式提交的测试业务操作;
- 应用错误率、连接失败和数据库锁等待。
如果备用库存在明显复制延迟,应在提升前由业务负责人决定是否接受对应RPO;不能在切换后才发现最近提交的数据尚未到达备用库。
失败处理和回滚边界
1. 配置阶段失败
如果修改配置后数据库无法启动:
- 保留错误日志和失败时间;
- 恢复变更前的配置备份;
- 检查配置项拼写、路径、权限和磁盘空间;
- 重新启动并确认原有业务状态;
- 不要继续配置复制,直到主库和备用库的基础服务恢复。
恢复配置只能回到服务层状态,不能撤销已经产生的数据库事务。因此主库配置回滚后,仍需检查二进制日志和GTID状态。
2. 复制建立失败
如果Replica_IO_Running不是Yes:
- 优先检查备用库到主库的地址、端口、账号来源限制和TLS证书;
- 查看
Last_IO_Error; - 确认主库二进制日志已开启且仍保留所需事务;
- 不要反复执行
RESET REPLICA ALL掩盖现场。
如果Replica_SQL_Running不是Yes:
- 查看
Last_SQL_Error; - 判断是表结构不一致、权限错误、主键冲突还是数据损坏;
- 先保存复制状态和错误日志;
- 不要直接跳过事务,除非已经确认该事务可以安全丢弃并得到业务批准。
如果备用库初始化错误或数据差异较大,通常应重新生成一致性备份并重建备用库,而不是在生产环境手工修改大量数据。
3. 切换后发现业务异常
如果新主库尚未接受任何业务写入,可以暂时停止应用流量并切回旧入口,但仍需确认旧主库没有被并行写入。
如果新主库已经接受写入,不能简单把应用切回旧主库。此时两边可能存在不同GTID和不同业务数据,强行切回会造成新写入丢失或数据分叉。正确做法是:
- 继续隔离旧主库;
- 保留新主库作为当前唯一写入源;
- 修复应用连接、权限或结构问题;
- 以新主库为源重新初始化旧主库;
- 重新建立反向的主备关系;
- 完成数据校验后,才把旧主库重新纳入备用池。
观察窗口和回滚触发条件
变更完成后应设置明确的观察窗口,观察时间应覆盖业务高峰、定时任务和连接池重连过程。重点监控:
- 复制IO线程和SQL线程;
Last_IO_Error、Last_SQL_Error;- GTID执行进度和复制延迟;
- 主库二进制日志增长速度及磁盘剩余空间;
- 应用连接失败、事务回滚和锁等待;
- 备用库是否仍被应用误写;
- 备份任务是否能够从当前主库正常执行。
满足以下任一条件,应暂停扩大流量并按预定方案回滚或重新切换:
- 旧主库无法被可靠隔离;
- 备用库复制线程异常且无法确认数据边界;
- 业务探针读取不到刚刚提交的数据;
- 应用仍有部分连接指向旧主库;
- 新主库出现持续增长的事务错误、权限错误或数据校验差异;
- 备用库磁盘、日志或复制延迟达到业务设定的风险阈值。
只有在观察窗口内复制、应用写入、备份和错误日志均符合预期,才可以把本次主备变更标记为完成。旧主库在重新加入之前,必须经过隔离、数据重建和复制验证,不能因为服务能够启动就直接恢复为备用节点。