MGR 组复制搭建手册(MySQL/MariaDB)※
本文介绍如何在多节点上搭建 MySQL/MariaDB 的 MGR 组复制(Group Replication),采用单主(single-primary)模式。覆盖:开启前置条件、数据准备(备份预灌 vs 增量恢复)、核心开启流程、验证与常见坑。GreatSQL / GreatDB 的特定差异见文末补充。
拓扑示例:3 个节点(host-a 为初始主库,host-b / host-c 为新增从库),组内节点通过专用端口互连。下文以 NODE_A:33061、NODE_B:33061、NODE_C:33061 示意,实际替换为各节点 IP。
一、前置条件(硬性,缺一不可)※
1.1 基础参数:所有节点全部 ON※
MGR 本质是基于 GTID 的复制,以下三个参数是运行前提,配置在 my.cnf 后需重启生效。集群中任一节点不满足都无法加入组。
-- 各节点分别确认:
SHOW VARIABLES WHERE Variable_name IN ('gtid_mode','enforce_gtid_consistency','log_slave_updates');
-- 期望三个值均为 ON与 SET gtid_purged 的区别:这三个参数是「开启 GTID 机制」的开关,只保证"能用 GTID",不会让预灌/已恢复的数据自动带上 GTID 记录。数据一致性靠后文的
SET GLOBAL gtid_purged(见 §4.2)。两者缺一不可。
1.2 各节点 server_id 唯一※
所有节点的 server_id 必须互不相同,否则组复制报错。
1.3 复制账号(含 BACKUP_ADMIN)※
-- 各节点分别执行(用 IF NOT EXISTS 避免重复报错)
CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY '密码';
ALTER USER 'repl'@'%' IDENTIFIED WITH MYSQL_NATIVE_PASSWORD BY '密码';
GRANT REPLICATION SLAVE, BACKUP_ADMIN ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;坑:若此前(如普通复制阶段)已建过同名账号,直接用
CREATE USER会报ERROR 1396 ... already exists中断操作。务必用CREATE USER IF NOT EXISTS或改为ALTER USER+GRANT。
1.4 组复制插件(必须)+ clone 插件(可选)※
-- 各节点确认:group_replication 必须 ACTIVE
SHOW PLUGINS; | grep group_replication
-- clone 插件可选(见下方说明),非必需
SHOW PLUGINS; | grep clonegroup_replication 为组复制本体,必须处于 ACTIVE。clone 插件非必需——它只在「加入节点与组数据不一致、需要从 donor 全量克隆补齐」时才用到;若数据已通过备份预灌一致(见 §3 方式 A),用增量恢复即可,完全不需要 clone。因此 clone 仅作为可选/兜底手段。
1.5 端口互通※
- MGR 专用端口(如 33061):组内节点 group communication 互连,所有节点互相放通;
- MySQL 业务/复制端口(如 3306):节点间互通。
1.6 内存核定(关键校验)※
致命坑:
innodb_buffer_pool_size必须按各节点自身物理内存核定(一般建议 ≤70%),并预留 OS、性能库、恢复进程的内存余量。若某节点内存偏小而照搬大节点的 buffer pool,该节点可能 起不来或 OOM。上线前先确认每台服务器物理内存,再分别设置。
1.7 my.cnf MGR 参数段(各节点追加,group_name / group_seeds 全局一致)※
# MGR(追加到 [mysqld] 段)
loose-plugin_load_add = 'group_replication.so'
loose-group_replication_group_name = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa1" # ⚠占位符,上线前换真实UUID
loose-group_replication_local_address = "NODE_A_IP:33061" # 各节点填自己的IP
loose-group_replication_group_seeds = "NODE_A_IP:33061,NODE_B_IP:33061,NODE_C_IP:33061"
loose-group_replication_communication_stack = "XCOM"
loose-group_replication_recovery_use_ssl = OFF
loose-group_replication_ssl_mode = DISABLED
loose-group_replication_start_on_boot = OFF
loose-group_replication_bootstrap_group = OFF
loose-group_replication_exit_state_action = READ_ONLY
loose-group_replication_flow_control_mode = "DISABLED"
loose-group_replication_single_primary_mode = ON
loose-group_replication_enforce_update_everywhere_checks = OFF
loose-group_replication_majority_after_mode = ON
loose-group_replication_communication_max_message_size = 10M
loose-group_replication_arbitrator = OFF
loose-group_replication_single_primary_fast_mode = 1
loose-group_replication_request_time_threshold = 100
loose-group_replication_primary_election_mode = GTID_FIRST
loose-group_replication_unreachable_majority_timeout = 0
loose-group_replication_member_expel_timeout = 5
loose-group_replication_autorejoin_tries = 288
loose-group_replication_recovery_get_public_key = ON
loose-group_replication_donor_threshold = 100坑:①
group_name是占位符,上线前必须换成真实随机 UUID,且所有节点保持一致;②member_expel_timeout=5偏激进,网络抖动可能误踢节点,生产建议放宽到 10~20;③ 改参数后需systemctl restart重启实例生效(服务名按实际 systemd 单元,如mysql/mysqld)。
二、节点安装与初始化(MySQL/MariaDB)※
MGR 组复制要求 MySQL 8.0.x(或兼容发行版)或 MariaDB 支持组复制的版本。全新节点需安装并初始化数据库服务;若某节点已有数据(如既有主库),则跳过初始化,直接保留其数据作为组内 PRIMARY 起点。
# MySQL 8.0:初始化数据目录 + 启动(以通用 my.cnf 配置为准,详见文末 GreatSQL 差异)
mysqld --initialize-insecure --user=mysql
systemctl start mysql # 或 mysqld,按安装方式
mysql -uroot -e "SELECT VERSION();"MGR 各节点用同一版本、同一 MGR 参数配置,确保组内兼容。节点数据不一致的处理见第三节。
三、数据准备与恢复※
两种方式把既有数据带进新节点:A. 备份预灌(快,适合大库)或 B. 靠 MGR 增量/克隆恢复(慢,数据全从 PRIMARY 拉)。本文以 A 备份预灌为主,并说明配套的 GTID 一致性步骤。
3.1 从现有主库备份※
# 从现有数据源用 mydumper 导出(示例:连源端)
mkdir -p /data/dbbak/$(date +%Y%m%d)
nohup /usr/bin/mydumper -u root -p '密码' -h 源IP --less-locking \
--threads=8 -s 10000000 --chunk-filesize=1024 \
--outputdir=/data/dbbak/$(date +%Y%m%d) --compress=1 --ignore-sysdb=1 --verbose=3 \
>> /tmp/backup.log 2>&1 &3.2 记录完整 GTID 集合(MGR 一致性关键,勿漏)※
# 在数据源(将作为 PRIMARY 的节点)执行一次,记下输出填 §4.2 的 <GTID>
mysql --login-path=default-root -N -e "SELECT @@GLOBAL.gtid_executed;"
# 形如:b3d0abcd-xxxx-xxxx-xxxx-xxxxxxxxxxxx:1-NNNNN关于备份的 metadata:
mydumper的 metadata 只稳记Log:/Pos:(供普通复制位点用);是否含 GTID 字段(Executed_Gtid_Set)取决于版本,不可依赖。最稳就是上面这条SELECT @@GLOBAL.gtid_executed。可核对:grep -i gtid /data/dbbak/<日期>/metadata。
3.3 新节点恢复数据(myloader)※
nohup /usr/bin/myloader -u root -p '' -S mysql.sock \
--threads=8 --directory=/data/dbbak/$(date +%Y%m%d) \
--verbose=3 --overwrite-tables >> /tmp/restore.log 2>&1 &不加 -e:恢复事务不进 binlog,节点不记录恢复事务——这正是为什么它的 GTID 记录为空、必须靠 §4.2 手动 SET gtid_purged 补齐。若还要再挂级联从库才加 -e。
四、开启组复制(核心流程)※
4.1 各节点加载 MGR 参数并重启※
systemctl restart mysql # 服务名按实际(如 mysqld/mariadb)
mysql -uroot -e "SHOW PLUGINS;" | grep group_replication # 必须 ACTIVE(clone 可选,见 §1.4)
# 再次确认各节点三参数 ON(§1.1)4.2 各节点建账号 + 清理(注意 PRIMARY 禁止 RESET MASTER)※
-- 各节点:建/改 repl 账号(见 §1.3)
-- 新节点(从库角色):清理残留,并补齐完整 GTID
RESET MASTER;
RESET SLAVE ALL;
SET GLOBAL gtid_purged = '<GTID>'; # <GTID>=§3.2 记下的完整GTID;声明"历史已恢复",加入组才只拉增量、不重放已灌数据
-- PRIMARY(有数据的节点):禁止 RESET MASTER / RESET SLAVE ALL(会清空 binlog 与 GTID),直接配 recovery 通道
-- 各节点:配置 recovery 通道
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='密码' FOR CHANNEL 'group_replication_recovery';为什么必须有 SET gtid_purged:预灌的数据是直接装载的,新节点
gtid_purged为空、gtid_executed最多只有增量,缺失数据源完整历史。不 SET 的话,加入组时组会判定该节点缺全部历史,由 donor 整段重放 → 撞上已存在数据(表已存在/主键重复)→ recovery 失败、节点永远无法 ONLINE。SET 后声明"历史已有",加入时只拉增量。
4.3 引导并加入集群※
-- 步骤①:在初始节点(将成为 PRIMARY)引导集群
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
-- 步骤②:其余节点加入
START GROUP_REPLICATION;引导顺序固定:先 PRIMARY 再其他节点。建议先只加入一个从库、验证 ONLINE 并追平后再加第三个,避免一次全加难定位。
bootstrap_group=ON用完必须置回 OFF,防止误重启重复引导产生脑裂(多主脑裂)。
4.4 兜底/回滚※
若组复制搭建失败:a) 先查 33061 端口连通性与 repl 账号权限;b) 必要时清理该节点状态后重新
START GROUP_REPLICATION;c) 若坚持普通复制兜底,需从干净状态重新装载数据并重建复制(不要在未清理 GTID/relay 的情况下与组复制混用)。
五、验证※
SELECT * FROM performance_schema.replication_group_members;
-- 期望:各节点 MEMBER_STATE 均 ONLINE,PRIMARY 一个,其余 SECONDARY
SELECT * FROM performance_schema.replication_group_member_stats\G
-- 期望:各节点事务数一致(已追平)
-- 读写验证(单主下仅在 PRIMARY 可写)
-- PRIMARY 执行:
CREATE TABLE mysql._repl_test (id INT PRIMARY KEY);
INSERT INTO mysql._repl_test VALUES (1);
-- SECONDARY 执行:
SELECT * FROM mysql._repl_test; -- 期望查到 id=1六、注意事项汇总(易踩坑清单)※
| # | 事项 | 影响 |
|---|---|---|
| 1 | SET GLOBAL gtid_purged 必须有(§4.2),且在 gtid_executed 为空、无复制通道时执行(RESET 后正好满足) | 缺 → 加入组重放历史撞数据,无法 ONLINE |
| 2 | 各节点内存需足够,innodb_buffer_pool_size 按各机实际内存核定 | 不够 → 起不来/OOM |
| 3 | CREATE USER IF NOT EXISTS,避免与已有 repl 账号重复 | 重复 → ERROR 1396 中断 |
| 4 | PRIMARY 节点禁止 RESET MASTER / RESET SLAVE ALL | 执行 → 清空 binlog/GTID,全组无法追位点 |
| 5 | group_name 占位符须换成真实 UUID,各节点一致 | 不一致 → 组无法互相识别 |
| 6 | member_expel_timeout=5 偏激进 | 抖动误踢 → 可放宽 10~20 |
| 7 | binlog 保留时长(如 7 天)要足够 | 从节点宕机超期 → donor binlog 已清,recovery 失败 |
| 8 | MGR 专用端口 + MySQL 端口放通 | 不通 → 组通信/恢复失败 |
| 9 | kill_idle_transaction | 低风险:可能杀掉 recovery 空闲事务(留意) |
| 10 | metadata 提取位点建议锚行首(如 grep '^Log:'/'^Pos:') | 误匹配 → 位点错误 |
七、补充:GreatSQL / GreatDB 差异※
GreatSQL 是兼容 MySQL 8.0 的国产分支,MGR 参数、GTID 机制、开启/验证流程与 MySQL/MariaDB 完全一致,本篇主体可直接套用。差异集中在安装部署与服务名。以下为实测的具体步骤。
7.1 安装路径与数据目录※
# GreatSQL 解压安装(路径/服务名按实际)
tar xf GreatSQL-8.0.32-XX-Linux-glibc2.28-x86_64.tar.xz -C /usr/local/
ln -s /usr/local/GreatSQL-8.0.32-XX-Linux-glibc2.28-x86_64 /usr/local/greatsql
mkdir -p /data/GreatSQL/ && chown -R mysql:mysql /data/GreatSQL/ && chmod -R 700 /data/GreatSQL/
# 初始化(空密码)+ 启动
/usr/local/greatsql/bin/mysqld --initialize-insecure --user=mysql
systemctl start greatsql
mysql -uroot -S /data/GreatSQL/mysql.sock -e "SELECT VERSION();"7.2 服务名※
systemctl restart greatsql # GreatSQL 专用服务名,配置生效需重启若用 GreatSQL 部署,主体中的 systemctl restart mysql 改为 systemctl restart greatsql。
7.3 备份工具 mydumper / myloader※
# 备份:从现有主库导出
nohup /usr/bin/mydumper -u root -p '密码' -h 源IP --less-locking \
--threads=8 -s 10000000 --chunk-filesize=1024 \
--outputdir=/data/dbbak/$(date +%Y%m%d) --compress=1 --ignore-sysdb=1 --verbose=3 \
>> /tmp/backup.log 2>&1 &
# 恢复:到新节点
nohup /usr/bin/myloader -u root -p '' -S /data/GreatSQL/mysql.sock \
--threads=8 --directory=/data/dbbak/$(date +%Y%m%d) \
--verbose=3 --overwrite-tables >> /tmp/restore.log 2>&1 &mydumper/myloader 是通用的 MySQL逻辑备份工具(非 GreatSQL 专有),但 GreatSQL 环境常用它做大数据量迁移;路径 /usr/bin/mydumper 为编译安装默认。