跳到主内容

MGR与MHA选型实战:主库宕机RTO与零丢失权衡 ​

问题背景:高可用不是只看能不能切换 ​

MySQL 主库宕机后,业务最关心两件事:多久恢复写入,也就是 RTO;恢复后会不会丢事务,也就是 RPO。MHA 曾是传统主从架构中最常见的高可用方案,而 MGR(Group Replication)基于 Paxos 协议提供组复制能力,更适合追求自动选主和数据一致性的场景。但二者都不是“装上就高可用”:MHA 依赖外部管理器和中继日志补齐,MHA 本身项目维护节奏较慢;MGR 对网络、事务模型、参数版本要求更严格,大事务或网络抖动可能触发频繁节点驱逐。

在交易、订单、账户类系统中,通常希望主库故障后做到数据零丢失;在日志、报表、营销活动等场景中,更看重快速恢复写入,可接受少量已提交事务丢失。因此选型不能只比较“谁自动”,而要结合 RTO、RPO、运维复杂度、应用兼容性和现有架构。

方案对比:MGR 与 MHA 的核心差异 ​

MHA:异步复制下的补偿式切换 ​

经典 MHA 由 MHA Manager、MHA Node、主库和若干从库组成。主库故障时,Manager 先确认故障,再从从库中寻找最新位点,把其他从库的差异 relay log 补齐到候选主库,最后切换 VIP、更新路由或调用脚本通知应用。

它的优点是兼容传统 binlog 复制,改造成本低;缺点也明显:故障检测和切换依赖外部脚本,RTO 通常在 10~60 秒;如果主库服务器彻底损坏且 binlog 没有及时传到从库,仍可能丢失已提交事务。半同步复制可以降低丢失概率,但在超时退化为异步后,并不能严格保证零丢失。

MGR:多数派确认后的一致性切换 ​

MGR 把事务广播、冲突检测、组成员管理和选主内置到复制插件中。单主模式下,主库写入需要获得多数节点确认;主库故障时,剩余多数派可以自动选举新主。配合 group_replication_consistency=BEFORE_ON_PRIMARY_FAILOVER 或更强一致性级别,新主在承接流量前会处理完复制队列,从而避免切换后读到旧数据。

MGR 的优势是自动化程度高、一致性语义清晰,常见 RTO 可做到 5~20 秒;代价是至少建议 3 节点、网络延迟低且稳定,所有表必须有主键,事务写入集合不能过大,并且对 MyISAM、串行隔离级别等特性支持有限。

主库故障检测MHA外部探测MGR多数派视图确认不可用避免网络分区误切MHA补齐relay log选最新从库并改路由MGR自动选主多数派提交保证一致性验证RTO、RPO与应用重连

选型决策框架:按 RPO 优先级选择 ​

如果业务明确要求“已返回成功的事务不能丢”,优先选择 MGR 三节点或更多节点的单主模式,并确保故障后仍保留多数派。若机房条件只能做两地两中心,无法容忍第三节点,MGR 并不能在单机房整体故障时靠少数派自动恢复;此时 MHA 加半同步、备份和明确 RPO 可能更现实。

如果历史系统大量使用无主键表、MyISAM、长事务或跨网络部署,迁移 MGR 的改造成本会比较高。此时可继续使用 MHA,但必须补齐监控、VIP/Proxy 切换、只读从库位点校验和主库 binlog 归档。

维度MHAMGR选型建议
自动选主依赖 Manager 与脚本内置组成员管理自动化优先选 MGR
RTO通常 10~60 秒常见 5~20 秒还要计入连接池恢复时间
RPO异步复制可能丢数据多数派提交可做到零丢失交易类优先 MGR
改造成本低,兼容传统主从较高,有主键和事务限制老库可先 MHA 后迁移
网络要求相对宽松低延迟、稳定、时钟可靠跨地域慎用 MGR

MGR 实操:搭建单主高可用并验证切换 ​

以下示例使用 MySQL 8.0,三节点地址分别为 10.0.0.11、10.0.0.12、10.0.0.13,端口为 3306。先在每个节点配置关键参数:

ini
# my.cnf 关键配置
sync_binlog=1
innodb_flush_log_at_trx_commit=1
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
log_replica_updates=ON
transaction_write_set_extraction=XXHASH64
plugin_load_add='group_replication.so'
group_replication_group_name='aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
group_replication_local_address='10.0.0.11:33061'
group_replication_group_seeds='10.0.0.11:33061,10.0.0.12:33061,10.0.0.13:33061'
group_replication_single_primary_mode=ON
group_replication_start_on_boot=OFF
group_replication_bootstrap_group=OFF

第一个节点引导组:

sql
-- 第一个节点初始化 MGR 组
SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

CHANGE REPLICATION SOURCE TO
  SOURCE_USER='repl',
  SOURCE_PASSWORD='Repl@123456'
  FOR CHANNEL 'group_replication_recovery';

SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

第二、第三个节点创建同样的复制账号后直接加入:

sql
-- 其他节点加入组
CHANGE REPLICATION SOURCE TO
  SOURCE_USER='repl',
  SOURCE_PASSWORD='Repl@123456'
  FOR CHANNEL 'group_replication_recovery';

START GROUP_REPLICATION;

-- 查看组成员与主库
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;

为了验证故障后的一致性,建立带主键的订单表。MGR 环境中所有需要写入的表都建议显式定义主键,否则冲突检测和变更追踪会受影响。

sql
CREATE DATABASE IF NOT EXISTS trade DEFAULT CHARACTER SET utf8mb4;
USE trade;

CREATE TABLE orders (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
  amount DECIMAL(12,2) NOT NULL COMMENT '订单金额',
  status VARCHAR(20) NOT NULL DEFAULT 'CREATED' COMMENT '订单状态',
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  KEY idx_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表';

-- 主库故障切换后,新主在处理旧队列前会阻塞新请求
SET GLOBAL group_replication_consistency='BEFORE_ON_PRIMARY_FAILOVER';

INSERT INTO orders(user_id, amount, status)
VALUES (1001, 99.50, 'PAID');

SELECT id, user_id, amount, status, created_at
FROM orders
WHERE user_id = 1001
ORDER BY created_at DESC
LIMIT 10;

演练时不要直接 kill -9 数据库进程后立刻观察应用,而应同时记录故障命令时间、新主选出时间、Proxy 路由生效时间和应用第一批成功写入时间。数据库选出新主不等于业务已经恢复,连接池如果仍持有旧主连接,RTO 可能被明显拉长。

MHA 实操:把 RPO 风险控制在可观测范围内 ​

MHA 更适合传统一主多从。建议主从使用 GTID、ROW 格式 binlog、半同步复制,并部署独立管理节点。关键不是 masterha_check_ssh 和 masterha_check_repl 通过,而是要反复演练以下链路:故障确认、候选主选择、relay log 补齐、写禁旧主、VIP 漂移、应用重连。

半同步参数可按业务容忍度配置:

sql
-- 主库:至少等待一个从库收到 binlog 后才返回提交成功
SET GLOBAL rpl_semi_sync_master_enabled=1;
SET GLOBAL rpl_semi_sync_master_timeout=3000;  -- 3秒后可退化为异步
SET GLOBAL rpl_semi_sync_master_wait_for_slave_count=1;
SET GLOBAL rpl_semi_sync_master_wait_point='AFTER_SYNC';

-- 从库
SET GLOBAL rpl_semi_sync_slave_enabled=1;

需要注意,rpl_semi_sync_master_timeout 超时后退化为异步,意味着网络异常期间仍可能产生未传到从库的主库事务。若业务要求绝对零丢失,不能简单把半同步等同于 MGR 多数派提交;更严格的做法是避免超时退化或在故障后通过 binlog 服务器、主库磁盘抢救和人工审计确认数据差异。

切换后应立即执行一致性检查:

sql
-- 在新主上查看复制账号与执行的 GTID 集合
SELECT @@global.gtid_executed AS executed_gtid;

-- 校验核心表最大主键和最近写入时间
USE trade;
SELECT COUNT(*) AS total_rows,
       MAX(id) AS max_order_id,
       MAX(created_at) AS last_created_at
FROM orders;

-- 检查是否存在无主键表,避免后续迁移 MGR 时返工
SELECT TABLE_SCHEMA, TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','sys','performance_schema','information_schema')
  AND TABLE_TYPE='BASE TABLE'
  AND ENGINE='InnoDB'
  AND TABLE_NAME NOT IN (
    SELECT TABLE_NAME
    FROM information_schema.STATISTICS
    WHERE TABLE_SCHEMA = TABLES.TABLE_SCHEMA
      AND NON_UNIQUE = 0
      AND SEQ_IN_INDEX = 1
  );

避坑总结:高可用切换最容易忽略的细节 ​

1. 只测数据库选主,不测连接池和代理 ​

常见现象是 replication_group_members 已显示新主,但应用仍持续报 Communications link failure 或写入只读节点。修正方法是在 JDBC、连接池和 Proxy 层统一配置旧连接快速回收、只读异常重试和写流量重新路由。例如连接池应开启 maxLifetime、连接可用性检测,并避免把故障节点长期缓存。

2. MGR 三节点部署在同一台物理机或同一机柜 ​

多数派只能容忍 MySQL 实例故障,不能容忍共同电源、机柜或机房故障。若三个节点共享同一台宿主机,宿主机宕机后组完全不可用。正确做法是三节点分散到不同物理机、机柜和供电域;跨机房时还要评估延迟和丢包,必要时增加第三机房仲裁节点。

3. 大事务导致切换慢或节点被驱逐 ​

MGR 中大事务会增大认证内存和网络传输时间,典型报错包括成员状态变为 UNREACHABLE、复制队列堆积、应用写入超时。应拆分为小批量事务,单事务影响行数尽量控制在数万以内,并监控 group_replication_transaction_been_ahead、认证队列和网络 RTT。DDL 也要避开业务高峰,使用在线 DDL 或 gh-ost 分批执行。

4. MHA 半同步超时后误以为零丢失 ​

半同步在超时后退化为异步,主库最后几秒事务可能没有到达任何从库。不要只看到 rpl_semi_sync_master_status=ON 就认为全程安全,还应监控异步等待次数、半同步从库数量和主从延迟。对资金类交易,应在架构上使用不退化的强同步策略或 MGR,并保留独立 binlog 归档用于审计。

5. 把 RTO 目标只定义为数据库恢复时间 ​

完整 RTO 应包含检测、选主、路由切换、连接池重建和应用补偿。建议把目标拆成四段监控:故障发现小于 3 秒,数据库选主小于 10 秒,代理路由生效小于 3 秒,业务恢复写入小于 30 秒。演练时用固定压测脚本持续写入,根据自增 ID、GTID 和业务订单号核对缺口,才能得到可信结果。

最终建议是:新系统和强一致交易链路优先采用三节点 MGR 单主模式;历史主从架构若短期无法完成主键、网络和事务模型改造,可保留 MHA,但必须用半同步、监控、binlog 归档和定期演练把风险显性化。高可用方案的价值不在架构图,而在主库宕机那一刻,业务是否能按预期的 RTO 恢复,并证明 RPO 真的为零。

免责声明

本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。