数据库运维11 min read次阅读

MySQL MGR 组复制高可用实战

为什么是 MGR

传统 MySQL 主从复制(异步/半同步)的三大痛点:

痛点 传统主从 MGR
故障切换 手动或靠 orchestrator/MHA 自动选主切换
数据一致性 异步可能丢数据 Paxos 强一致(多数派确认)
脑裂风险 高(需外部仲裁) 内核级防脑裂(多数派存活才可写)

MySQL Group Replication(MGR,5.7.17+ GA,8.0 成熟)基于 Paxos 变种(MySQL XCom) 实现多节点状态机复制,保证组内数据强一致,原生高可用。

核心概念

┌─────────────────────────────────────────────────┐
│            Group (组, 奇数节点推荐)               │
│                                                  │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐    │
│   │  Node-1   │   │  Node-2   │   │  Node-3   │    │
│   │  Primary  │   │ Secondary │   │ Secondary │    │
│   │ (可读写)  │   │ (只读)    │   │ (只读)    │    │
│   └────┬─────┘   └────┬─────┘   └────┬─────┘    │
│        │              │              │           │
│        └──────────────┼──────────────┘           │
│                 XCom (Paxos) 消息层              │
│         每个事务需多数派(≥2/3)确认才提交          │
└─────────────────────────────────────────────────┘
  • 单主模式(Single-Primary):只有一个节点可写,其余只读,自动选主(默认,最常用)
  • 多主模式(Multi-Primary):所有节点均可写,需处理冲突检测
  • XCom 层:基于 Paxos 的组通信引擎,负责节点发现、成员管理、消息有序广播
  • 多数派(Majority):N 节点组可容忍 (N-1)/2 节点故障;3 节点容忍 1 个宕机

部署前准备

# 三节点均执行(以 MySQL 8.0 为例)
# 1. 关闭冲突功能
-- 必须以下条件:
--   - 每张表必须有主键(无主键表无法在 MGR 中复制)
--   - 存储引擎必须为 InnoDB
--   - 必须开启 GTID
--   - 必须使用行格式 binlog(ROW)

# 2. 修改 my.cnf(每个节点不同 server_id 和 report_host)
cat >> /etc/my.cnf << 'EOF'
[mysqld]
server_id = 1                          # 各节点不同: 1/2/3
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
binlog_checksum = NONE                 # MGR 要求
master_info_repository = TABLE
relay_log_info_repository = TABLE
transaction_write_set_extraction = XXHASH64
log_slave_updates = ON
skip_slave_start = ON

# MGR 专用配置
plugin_load_add = 'group_replication.so'
transaction_isolation = 'READ-COMMITTED'   # 多主模式必须
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"  # 组UUID, 三节点一致
group_replication_start_on_boot = OFF
group_replication_local_address = "192.168.1.11:33061"   # 各节点本机IP:端口
group_replication_group_seeds = "192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061"
group_replication_bootstrap_group = OFF
group_replication_single_primary_mode = ON   # 单主模式
group_replication_enforce_update_everywhere_checks = OFF
report_host = 192.168.1.11                # 各节点本机IP
EOF

systemctl restart mysqld

组名必须是合法 UUID 格式,且三节点完全一致。建议用 SELECT UUID() 生成一个。

引导组复制

第一个节点(引导者)

-- 安装插件(如未自动加载)
INSTALL PLUGIN group_replication SONAME 'group_replication.so';

-- 创建复制专用用户
SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'ReplPass!2026' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
SET SQL_LOG_BIN=1;

CHANGE REPLICATION SOURCE TO
    SOURCE_USER='repl',
    SOURCE_PASSWORD='ReplPass!2026',
    SOURCE_HOST='192.168.1.11',       -- 本机
    SOURCE_PORT=3306,
    GET_SOURCE_PUBLIC_KEY=1
    FOR CHANNEL 'group_replication_recovery';

-- 引导组(仅第一个节点执行一次!)
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;

-- 确认成员状态
SELECT * FROM performance_schema.replication_group_members;
-- MEMBER_STATE 应为 ONLINE

其他节点(加入者)

INSTALL PLUGIN group_replication SONAME 'group_replication.so';

SET SQL_LOG_BIN=0;
CREATE USER 'repl'@'%' IDENTIFIED BY 'ReplPass!2026' REQUIRE SSL;
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
SET SQL_LOG_BIN=1;

CHANGE REPLICATION SOURCE TO
    SOURCE_USER='repl',
    SOURCE_PASSWORD='ReplPass!2026',
    SOURCE_HOST='192.168.1.11',       -- 任意在线成员均可
    SOURCE_PORT=3306,
    GET_SOURCE_PUBLIC_KEY=1
    FOR CHANNEL 'group_replication_recovery';

-- 直接加入(不要 bootstrap)
START GROUP_REPLICATION;

SELECT * FROM performance_schema.replication_group_members;

验证

-- 查看组成员与角色
SELECT
    MEMBER_ID, MEMBER_HOST, MEMBER_PORT,
    MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
-- MEMBER_ROLE: PRIMARY / SECONDARY

-- 查看主节点(单主模式)
SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'group_replication_primary_member';

故障自动切换

单主模式下,Primary 宕机后,MGR 自动从 Secondary 中选举新 Primary:

# 模拟 Primary 宕机
systemctl stop mysqld    # 在 Primary 节点执行

# 在存活节点查看(约 5-10 秒完成切换)
SELECT MEMBER_HOST, MEMBER_ROLE, MEMBER_STATE
FROM performance_schema.replication_group_members;
-- 原 Secondary 之一变为 PRIMARY,状态 ONLINE

切换过程:

  1. 故障节点被踢出组(超过 group_replication_member_expel_timeout 超时)
  2. 剩余节点重新选举 Primary(基于 UUID 字典序的优先级)
  3. 应用连接需重连到新 Primary(用 MySQL Router 或 VIP 透明切换)

配合 MySQL Router(透明路由)

# 安装 MySQL Router(应用层不需要知道哪个是 Primary)
mysqlrouter --bootstrap root@192.168.1.11:3306 --user=mysqlrouter
systemctl start mysqlrouter

# 应用连接 Router 的读写端口(默认 6446 写 / 6447 读)
# Router 自动把写请求路由到当前 Primary,读请求负载到 Secondary

推荐架构:MGR 三节点 + MySQL Router + 应用连接 Router 端口,实现完全透明的故障切换。

多主模式(谨慎使用)

-- 所有节点设置
SET GLOBAL group_replication_single_primary_mode = OFF;
SET GLOBAL group_replication_enforce_update_everywhere_checks = ON;

-- 重启组(先停所有节点,引导者改模式后重启)

多主模式要点:

  • 每个节点独立接受写,XCom 保证全局顺序
  • 冲突检测:同一行被两个节点同时修改 → 后提交的事务回滚(报错 ERROR 1180
  • 要求 READ-COMMITTED 隔离级别
  • 外键、级联操作、大事务在多主下风险高

经验法则:99% 场景用单主模式。多主仅适合写少、无跨节点同行冲突、需要就近写入的全球部署。

监控与告警

关键状态指标

-- 1. 成员健康
SELECT MEMBER_HOST, MEMBER_STATE
FROM performance_schema.replication_group_members;
-- 所有应为 ONLINE;出现 RECOVERING/OFFLINE/ERROR 告警

-- 2. 待应用的事务队列(延迟指标)
SELECT COUNT_TRANSACTIONS_IN_QUEUE
FROM performance_schema.replication_group_member_stats
WHERE MEMBER_ID = (SELECT VARIABLE_VALUE FROM performance_schema.global_status
                   WHERE VARIABLE_NAME='group_replication_primary_member');
-- 持续 > 0 表示有复制延迟

-- 3. 冲突/错误计数
SELECT COUNT_TRANSACTIONS_ROWS_VALIDATING, COUNT_CONFLICTS_DETECTED
FROM performance_schema.replication_group_member_stats;

Prometheus 监控

# mysqld_exporter 已暴露 MGR 指标
scrape_configs:
  - job_name: 'mysql-mgr'
    static_configs:
      - targets: ['192.168.1.11:9104', '192.168.1.12:9104', '192.168.1.13:9104']

# 关键告警
groups:
  - name: mysql-mgr
    rules:
      - alert: MGRMemberNotOnline
        expr: mysql_group_replication_member_state{state!="ONLINE"} == 1
        for: 1m
        labels: { severity: critical }

      - alert: MGRPrimaryMissing
        expr: count(mysql_group_replication_primary_member > 0) == 0
        for: 30s
        labels: { severity: critical }

      - alert: MGRReplicationLag
        expr: mysql_group_replication_transaction_queue > 100
        for: 2m
        labels: { severity: warning }

常见故障处理

节点意外退出后重新加入

-- 在退出节点执行
STOP GROUP_REPLICATION;
START GROUP_REPLICATION;
-- 若报认证错误,可能需要:
RESET SLAVE ALL FOR CHANNEL 'group_replication_recovery';
START GROUP_REPLICATION;

脑裂后网络分区恢复

-- 少数派分区节点会被设为 ERROR 状态,需手动恢复:
STOP GROUP_REPLICATION;
-- 确认多数派分区已选出新 Primary 且数据最新
START GROUP_REPLICATION;   -- 重新作为 Secondary 加入,自动追平数据

引导者永久丢失

-- 若原引导节点彻底不可用,在新 Primary 上无需 bootstrap
-- 只需确保 group_replication_bootstrap_group=OFF,正常 START 即可
-- 组 UUID 保持不变,新节点沿用原组名加入

与 InnoDB Cluster 的关系

MySQL InnoDB Cluster = MGR + MySQL Shell + MySQL Router 的完整高可用方案:

# 用 MySQL Shell 管理(比手动 SQL 简单)
mysqlsh
> dba.configureInstance('root@192.168.1.11:3306')
> var c = dba.createCluster('myCluster')
> c.addInstance('root@192.168.1.12:3306')
> c.addInstance('root@192.168.1.13:3306')
> c.status()

生产环境推荐直接用 InnoDB Cluster(Shell 管理),底层就是 MGR,但省去了手动配置引导、故障切换路由等繁琐步骤。

避坑清单

# 症状 解决
1 表无主键 节点报错退出组 MGR 要求每张表有主键;加主键后重新加组
2 binlog_checksum 未关 启动失败 binlog_checksum=NONE
3 引导节点误 bootstrap 两次 出现两个组/脑裂 仅第一个节点首次启动设 bootstrap_group=ON 一次
4 网络抖动频繁踢节点 成员反复 RECOVERING 调大 group_replication_member_expel_timeout(默认 5s→30s)
5 多主模式写冲突 事务回滚 ERROR 1180 避免跨节点同写一行;或改回单主
6 单主模式应用直连旧 Primary 写入失败 应用经 MySQL Router 或 VIP 连接,不直连 IP
7 大事务阻塞组 复制延迟飙升 拆分大事务;调大 group_replication_transaction_size_limit
8 节点数据严重落后 追平超时 用克隆插件(clone)重建节点,比增量追平快
9 SSL 未启用 复制用户明文 REQUIRE SSL + 配置组通信 SSL
10 偶数节点部署 脑裂时无法形成多数派 部署奇数节点(3/5),或加仲裁节点(Arbitrator)

总结

MySQL MGR 把"高可用"从外部工具(MHA/orchestrator)下沉到数据库内核,核心要点:

  1. Paxos 多数派确认保证强一致,原生防脑裂(少数派自动只读)
  2. 单主模式自动选主,配合 MySQL Router 实现透明故障切换
  3. 每张表必须有主键binlog_format=ROWbinlog_checksum=NONE 是硬性前提
  4. 奇数节点部署(3/5),容忍 (N-1)/2 故障
  5. 监控成员状态 + 事务队列是关键告警指标
  6. 生产推荐 InnoDB Cluster(Shell 管理),底层即 MGR 但更易用

选型对比:中小团队要强一致高可用 → MGR/InnoDB Cluster;需要异地多活、海量写入 → 考虑 TiDB;纯读扩展、容忍秒级延迟 → 传统一主多从 + ProxySQL 仍简单有效。

分享:

相关文章

评论区