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

香港服务器部署业务系统,如何用主备复制与恢复目标设计高可用方案

发布人:Minchunlin 发布时间:2026-10-01 21:37 阅读量:9

先确定目标状态:可切换,不等于自动恢复

香港服务器部署业务系统时,主备方案的核心不是“再准备一台服务器”,而是把单点故障拆开处理:业务入口、数据库写入节点、数据库副本、切换动作和恢复验证都要有明确边界。

一个稳妥的目标状态是:正常情况下,应用只向主数据库写入,备用数据库持续复制并保持只读;主库出现故障时,先阻断旧主库继续接收写入,再提升备用库,最后将应用连接切换到新主库。没有完成“隔离旧主库”这一步,不应直接提升备用库,否则两个节点可能同时写入,形成脑裂。

解释主库、备用库和应用入口在正常运行及故障切换中的职责关系与写入边界。

下面以两台香港服务器、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. 做一次受控切换演练

正式故障前应安排一次有维护窗口的切换演练,流程如下:

把受控主备切换演练的操作顺序和关键安全检查组织成可执行流程。

  1. 在应用入口启用维护模式或暂停写入;
  2. 确认主库没有新的业务写入;
  3. 记录主库最新GTID;
  4. 确认备用库已经执行到该GTID;
  5. 停止主库或隔离其数据库端口;
  6. 在备用库停止复制并提升为可写;
  7. 切换应用连接地址;
  8. 用业务探针验证登录、查询、写入和事务提交;
  9. 观察应用错误日志、数据库错误日志和连接池状态。

切换完成后,旧主库不能直接重新开放写入。它应先保持隔离,作为待重建的备用节点处理。

故障切换:先隔离旧主,再提升备用库

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. 配置阶段失败

如果修改配置后数据库无法启动:

  1. 保留错误日志和失败时间;
  2. 恢复变更前的配置备份;
  3. 检查配置项拼写、路径、权限和磁盘空间;
  4. 重新启动并确认原有业务状态;
  5. 不要继续配置复制,直到主库和备用库的基础服务恢复。

恢复配置只能回到服务层状态,不能撤销已经产生的数据库事务。因此主库配置回滚后,仍需检查二进制日志和GTID状态。

2. 复制建立失败

如果Replica_IO_Running不是Yes:

  • 优先检查备用库到主库的地址、端口、账号来源限制和TLS证书;
  • 查看Last_IO_Error;
  • 确认主库二进制日志已开启且仍保留所需事务;
  • 不要反复执行RESET REPLICA ALL掩盖现场。

如果Replica_SQL_Running不是Yes:

  • 查看Last_SQL_Error;
  • 判断是表结构不一致、权限错误、主键冲突还是数据损坏;
  • 先保存复制状态和错误日志;
  • 不要直接跳过事务,除非已经确认该事务可以安全丢弃并得到业务批准。

如果备用库初始化错误或数据差异较大,通常应重新生成一致性备份并重建备用库,而不是在生产环境手工修改大量数据。

3. 切换后发现业务异常

如果新主库尚未接受任何业务写入,可以暂时停止应用流量并切回旧入口,但仍需确认旧主库没有被并行写入。

如果新主库已经接受写入,不能简单把应用切回旧主库。此时两边可能存在不同GTID和不同业务数据,强行切回会造成新写入丢失或数据分叉。正确做法是:

  1. 继续隔离旧主库;
  2. 保留新主库作为当前唯一写入源;
  3. 修复应用连接、权限或结构问题;
  4. 以新主库为源重新初始化旧主库;
  5. 重新建立反向的主备关系;
  6. 完成数据校验后,才把旧主库重新纳入备用池。

观察窗口和回滚触发条件

变更完成后应设置明确的观察窗口,观察时间应覆盖业务高峰、定时任务和连接池重连过程。重点监控:

  • 复制IO线程和SQL线程;
  • Last_IO_Error、Last_SQL_Error;
  • GTID执行进度和复制延迟;
  • 主库二进制日志增长速度及磁盘剩余空间;
  • 应用连接失败、事务回滚和锁等待;
  • 备用库是否仍被应用误写;
  • 备份任务是否能够从当前主库正常执行。

满足以下任一条件,应暂停扩大流量并按预定方案回滚或重新切换:

  • 旧主库无法被可靠隔离;
  • 备用库复制线程异常且无法确认数据边界;
  • 业务探针读取不到刚刚提交的数据;
  • 应用仍有部分连接指向旧主库;
  • 新主库出现持续增长的事务错误、权限错误或数据校验差异;
  • 备用库磁盘、日志或复制延迟达到业务设定的风险阈值。

只有在观察窗口内复制、应用写入、备份和错误日志均符合预期,才可以把本次主备变更标记为完成。旧主库在重新加入之前,必须经过隔离、数据重建和复制验证,不能因为服务能够启动就直接恢复为备用节点。

目录结构
全文