☰
MySQL 8.4 MGR组复制三节点单主高可用搭建实战与排错
2026/9/26 12:28:25 网站建设 项目流程

如果你和我一样,手上的 MySQL 刚好是 8.4.7,又被要求尽快把主从高可用搭起来,那组复制(MySQL Group Replication,简称 MGR)基本是绕不开的选项。这篇文章是我从零开始搭三节点单主 MGR 的完整记录,包含 8.4 版本里和旧版本差异很大的初始化、SSL、插件加载细节,也把搭建时会遇到的典型报错和修复方法整理了,最后补充了 Docker、Kubernetes 环境下的落地要点。适合刚接手 MySQL 8.x 的 DBA、准备做高可用改造的架构师,以及那些看到一堆 group_replication_* 参数就头疼的同学。

1. 为什么这次我选了组复制而不是传统主从

1.1 传统主从复制哪里不够用

MySQL 的传统主从复制,本质是异步复制:主库把 binlog 推给从库,从库回放,主库并不确认从库是否真正拿到日志。正常场景下延迟可能只有几十毫秒,可一旦主库宕机,还没推出去的 binlog 就丢了。很多人为了缩小这个窗口上了半同步复制,但半同步在网络抖动时很容易退化成异步,而且主从切换还是要依赖 MHA、Orchestrator 这类第三方工具,脚本写得再小心,总有些边界情况处理不干净。

MGR 解决的就是这两件事:一是用共识协议保证日志顺序和成员状态统一,二是把“谁当主库”的决策从外部脚本搬到了数据库内部。它不是异步复制,也不是简单的半同步,而是组内每个节点都维护自己的 binlog,同时通过组通信层确认事务提交顺序,再执行冲突检测。只要多数派节点活着,整个组就能继续工作,主节点挂了会自动选出新主,数据不会因为“日志还没传过去”就直接丢。

我用 8.4.7 搭建前查过版本情况,MySQL 8.4 已经是 LTS 版本,组复制插件从 8.0 开始不断演进,到 8.4 这一代稳定性和性能都算成熟。三节点单主模式,既能做到故障自动切换,又不需要引入额外协调组件,比我自己二开一套高可用脚本要可靠得多。

1.2 MGR 单主和多主怎么选

MGR 有两种模式:单主(single-primary)和多主(multi-primary)。单主模式里同一时刻只有一个节点接受读写,其余节点是只读备库;多主模式允许所有节点写入。

标题虽然叫“主从搭建”,但从生产稳定性出发,我强烈建议你第一套先做单主。多主模式最大的坑是冲突检测:两个节点同时改同一行,哪怕最终能成功一个,业务侧也可能报主键冲突或更新丢失。真要上多主,业务必须把写入按维度拆分到不同节点,比如按用户 ID 分片、按区域分片,让同一条数据尽量只在同一个节点上写。这个改造量远超多数业务愿意付出的成本。

所以本文的配置都按单主模式来,相关参数是这两组:

group_replication_single_primary_mode=ON group_replication_enforce_update_everywhere_checks=OFF

这两行是配套的。如果开了 enforce_update_everywhere_checks,单主模式会被强制改成多主。第一次配 MGR 的人,最容易栽在这里。

2. 8.4.7 环境准备:安装初始化容易翻车的地方

2.1 安装包选择与依赖

我先说结论:能用官方二进制包就别用系统自带的发行版包,能在线装就选官方 Yum 仓库,离线环境用 RPM 包配合本地目录。

我这次三台机器都是 Rocky Linux 9,生产环境不允许直接连外网,所以走的离线 RPM 路线。需要准备这几类文件:

mysql-community-server-8.4.7-1.el9.x86_64.rpm mysql-community-client-8.4.7-1.el9.x86_64.rpm mysql-community-common-8.4.7-1.el9.x86_64.rpm mysql-community-libs-8.4.7-1.el9.x86_64.rpm mysql-community-client-plugins-8.4.7-1.el9.x86_64.rpm mysql-community-icu-data-files-8.4.7-1.el9.x86_64.rpm

安装前检查系统依赖,特别是 libaio。RPM 安装时缺依赖最常见的报错就是它:

yum install -y libaio ncurses-compat-libs perl rpm -ivh mysql-community-*.rpm

装完之后最好再确认一次版本,防止某台机器装歪了变成 8.0:

mysqld --version

如果是用官方 Yum 仓库在线安装,先确认仓库里默认的版本号是 8.4 而不是 8.0。这一步看起来不起眼,实际踩过的人不少,配了半天 MGR 参数,最后发现三个节点版本不一致,光排查就浪费半天。

2.2 初始化实例和 8.4 默认行为变化

RPM 安装完会自动创建 mysql 用户和 /var/lib/mysql 目录,但不代表实例已经初始化。8.4 版本的初始化命令和 8.0 基本一致:

mysqld --initialize --user=mysql

执行完后去 error log 里翻临时密码:

grep 'temporary password' /var/log/mysqld.log

然后启动服务:

systemctl start mysqld systemctl enable mysqld mysql -uroot -p

登录后第一步强制改密码:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'Your_Strong_Pass_2024';

这里要特别提醒 8.4 的几个默认行为变化:

  • 默认认证插件已经是 caching_sha2_password,不再是 mysql_native_password。8.4 里 mysql_native_password 插件默认不再加载,所以不要再用“IDENTIFIED WITH mysql_native_password BY” 这种写法去建复制账号,否则直接报错。
  • SSL 默认是开启的,实例会自动生成自签名证书。这个和旧版本差异很大,旧脚本里如果写了“关闭 SSL 以提升性能”之类的操作,在 8.4 上会遇到组复制 recovery 通道连不上。
  • 某些默认值也被调整过,比如 secure_file_priv 默认是 NULL,意味着 LOAD DATA INFILE 之类操作默认被限制。组复制不直接依赖它,但如果后续要从文件初始化数据,需要提前调好。

节点初始化完成后,建议先单独确认本机 root 能正常登录、socket 路径是什么,再往下走。否则后面一遇到 ERROR 2002,你会分不清是初始化失败还是配置写错。

2.3 三节点基础配置模板

我用的三台机器地址是 172.16.20.21、172.16.20.22、172.16.20.23,每台机器上 MySQL 版本统一是 8.4.7。MGR 三个节点至少要保证这些参数一致:

[mysqld] server_id=1 gtid_mode=ON enforce_gtid_consistency=ON binlog_format=ROW binlog_checksum=NONE log_slave_updates=ON relay_log_recovery=ON master_info_repository=TABLE relay_log_info_repository=TABLE transaction_write_set_extraction=XXHASH64 binlog_row_image=FULL plugin_load_add='group_replication.so' group_replication_group_name="6b8f499a-9f34-11ee-9b9e-005056a2b8df" group_replication_start_on_boot=OFF group_replication_bootstrap_group=OFF group_replication_single_primary_mode=ON group_replication_enforce_update_everywhere_checks=OFF group_replication_local_address="172.16.20.21:33061" group_replication_group_seeds="172.16.20.21:33061,172.16.20.22:33061,172.16.20.23:33061" group_replication_ip_allowlist="172.16.20.0/24" group_replication_exit_state_action=READ_ONLY loose-group_replication_recovery_use_ssl=ON loose-group_replication_recovery_ssl_verify_server_cert=OFF

不同节点只需要改四处:

节点server_idreport_hostgroup_replication_local_address
mysql-11172.16.20.21172.16.20.21:33061
mysql-22172.16.20.22172.16.20.22:33061
mysql-33172.16.20.23172.16.20.23:33061

group_replication_group_name 是组名,官方要求必须是合法 UUID,我习惯用SELECT UUID()生成一个,而不是手写。group_replication_start_on_boot 在生产上我故意设为 OFF,别小看这个默认值:如果设为 ON,节点重启后会自动尝试加入组,一旦组还没 bootstrap 或者网络抖动,会出现各种奇怪的日志。我宁可在服务启动后手动 START GROUP_REPLICATION,也不要让节点“自动归队”。

3. 单主组复制搭建全流程(三节点实战)

3.1 第一个节点:初始化组

三台机器把 my.cnf 改好并重启 mysqld 后,先用 root 登录第一台节点,确认插件已经加载:

SHOW PLUGINS;

看到 group_replication 状态为 ACTIVE 就说明 plugin_load_add 生效了。如果没看到,手动执行:

INSTALL PLUGIN group_replication SONAME 'group_replication.so';

接着创建组复制专用账号。这个账号至少需要 REPLICATION SLAVE、REPLICATION CLIENT、BACKUP_ADMIN、CLONE_ADMIN,8.4 里还要单独给 GROUP_REPLICATION_STREAM,否则后面恢复通道会报权限不足:

CREATE USER 'mgr_repl'@'%' IDENTIFIED BY 'Mgr_Repl_Pass_2024'; GRANT REPLICATION SLAVE, REPLICATION CLIENT, BACKUP_ADMIN, CLONE_ADMIN, GROUP_REPLICATION_STREAM ON *.* TO 'mgr_repl'@'%'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE ON *.* TO 'mgr_repl'@'%';

然后在每个节点上把复制通道指向这个账号。8.4 里旧命令 CHANGE MASTER TO 还保留,但会打 deprecated 告警,直接用新写法:

CHANGE REPLICATION SOURCE TO SOURCE_USER='mgr_repl', SOURCE_PASSWORD='Mgr_Repl_Pass_2024' FOR CHANNEL 'group_replication_recovery';

最后回到第一台节点,先把 bootstrap 开关打开,再启动组:

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

必须注意:bootstrap_group 只应该在第一台节点第一次启动组时打开,其他节点永远不要开。这个开关说白了就是告诉其他成员“我是源头”,一旦多开,就会出现多个“源头”,轻则组分裂,重则数据错乱。

启动后立即查成员状态:

SELECT member_id, member_host, member_port, member_state, member_role FROM performance_schema.replication_group_members;

正常情况下第一台节点应该显示 ONLINE、PRIMARY。

3.2 第二、第三个节点加入

第二、第三台节点需要完全相同的前置步骤:改好 my.cnf、建复制账号、配置 group_replication_recovery 通道。然后什么都不用 bootstrap,直接执行:

START GROUP_REPLICATION;

如果一切顺利,一两秒后就能看到三台节点全部 ONLINE。需要注意的是,第二、第三台节点第一次加入时,会从现有 PRIMARY 节点拉取数据回放,这个过程叫分布式恢复。只要 PRIMARY 上 binlog 还没有被清理,新节点就能自动补全数据。

如果第二、第三节点曾经是独立实例,自己产生过 GTID 事务,加入时会报“GTID 不一致”的错误。这时候不要硬加,最省事的办法是把该节点数据目录清掉重新初始化,让它以一个“空白实例”身份加入;也可以使用 Clone 插件从 PRIMARY 打底:

SET GLOBAL clone_valid_donor_list = '172.16.20.21:3306'; CLONE INSTANCE FROM 'mgr_repl'@'172.16.20.21' IDENTIFIED BY 'Mgr_Repl_Pass_2024';

Clone 完成后实例会自动重启,重启后再配置 recovery 通道,然后 START GROUP_REPLICATION。这套流程对数据量大的节点非常友好,不用手动导出一晚上才同步完。

3.3 功能验证与日常维护

三节点全部 ONLINE 后,我在 PRIMARY 上建了一个测试库和一张带主键的表,插入几条数据:

CREATE DATABASE app_test; USE app_test; CREATE TABLE t_user ( id INT PRIMARY KEY, name VARCHAR(64) ); INSERT INTO t_user VALUES (1, 'zhangsan'), (2, 'lisi');

然后分别在两个备库上查询,能看到同样的数据,说明异步恢复通道和 applier 都正常。注意 MGR 强制要求业务表必须有主键,原因也很好理解:组复制要判断事务是否冲突,需要记录行级别写集并做冲突校验,没有主键就没法精确标识行。

接着模拟一次主库故障。我直接在主库上执行 STOP GROUP_REPLICATION,模拟异常退出:

STOP GROUP_REPLICATION;

等待几秒,再看成员状态,会看到剩余两台节点自动选出了新的 PRIMARY。整个过程不需要任何手工切换脚本,业务连接如果走了 VIP 或负载均衡,只需要把读写入口重新指向新主即可。

日常维护时最常用的两个查询:

-- 查看组成员健康度 SELECT * FROM performance_schema.replication_group_members; -- 查看每个成员的事务统计与流量控制 SELECT * FROM performance_schema.replication_group_member_stats\G

4. 排错实录:搭建和压测中踩过的坑

4.1 ERROR 2002:socket 连不上

搭建过程中最容易遇到的问题不是组复制本身,而是客户端连不上本地 MySQL,报错长这样:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'

这种报错八成是 socket 路径不一致。MySQL 客户端默认去 /tmp/mysql.sock 找 socket,但 8.4 的服务端 socket 位置通常配置成了 /var/run/mysqld/mysqld.sock。解决办法有两个方向:

先检测 socket 实际位置:

mysqladmin --socket=/var/run/mysqld/mysqld.sock ping

能通就直接连:

mysql -uroot -p -S /var/run/mysqld/mysqld.sock

长期方案是在 my.cnf 的 [client] 区段显式指定 socket 路径,避免不同客户端工具读取到不同配置。这不是 MGR 特有的问题,但组复制搭建要反复登录、反复查状态,socket 对不上会浪费很多不必要的时间。

4.2 recovery 阶段的 SSL 报错

第二台节点执行 START GROUP_REPLICATION 后,成员状态一直停留在 RECOVERING,查看恢复通道错误:

SELECT CHANNEL_NAME, SERVICE_STATE, LAST_ERROR_NUMBER, LAST_ERROR_MESSAGE FROM performance_schema.replication_connection_status WHERE CHANNEL_NAME = 'group_replication_recovery'\G

我遇到的报错是:

ERROR 2026 (HY000): SSL connection error: error during SSL handshake

原因在于 8.4 默认开启 SSL,复制恢复通道默认也要求走 SSL,但节点之间可能因为证书、TLS 版本不一致导致握手失败。我的处理方式是在三个节点的 my.cnf 里都显式加上这两行:

loose-group_replication_recovery_use_ssl=ON loose-group_replication_recovery_ssl_verify_server_cert=OFF

注意不要漏掉前面的 loose- 前缀。loose-这个前缀的作用是:如果当前实例还没加载 group_replication 插件,mysqld 启动时不会因为识别不了这个变量而报错,而是把它当作未知参数忽略掉。

另外,如果复制账号是 caching_sha2_password 认证,并且恢复通道没有走 SSL,还会出现“公钥检索失败”之类的报错。8.4 下要么保证 SSL 开启,要么临时打开:

SET GLOBAL group_replication_recovery_get_public_key=ON;

实测下来还是走 SSL 最干净,不用去纠结公钥交换的问题。

4.3 成员一直 RECOVERING,卡住不动

节点状态持续显示 RECOVERING,但 gr_member_state 一直不到 ONLINE,除了 SSL 外,最常见原因有三个:

第一个是 donor 节点 binlog 已经清理过,新节点追不到完整事务。比如数据量大的实例、binlog 保留时间很短,新节点加入时拉取不到起点。解决思路是尽量用 Clone 插件打底,或者临时调大 binlog 保留时间:

binlog_expire_logs_seconds=86400

需要提前确认足够的数据保留窗口,再把新节点加入。

第二个是新节点无法连接 donor 的 MGR 通信端口 33061。组复制节点间的通信除了默认的 3306,还依赖 group_replication_local_address 指定的端口,防火墙或安全组经常只放行 3306 而忘了 33061。检查方法是在备节点上直接:

telnet 172.16.20.21 33061

第三个是复制账号权限不足。如果 group_replication_recovery 通道使用的账号缺少 BACKUP_ADMIN 或 CLONE_ADMIN,加入时会报类似“ERROR 3092”的信息。我把必要的权限列一遍,直接照着授权即可:

GRANT BACKUP_ADMIN, CLONE_ADMIN, GROUP_REPLICATION_STREAM, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'mgr_repl'@'%';

4.4 丢节点、脑裂和强制恢复

组复制里最忌讳的就是“随便找一台机器 STOP GROUP_REPLICATION 再 START”。如果同一时刻有多个节点退出,剩下来的节点不足以形成多数派,组会进入只读状态,保住数据一致性是第一优先级。

如果组内剩下两个节点,通常还能维持,因为三节点组的多数派是 2。如果只剩一个节点,或者因为网络分区两边都以为自己是“少数派”,就必须用强制重配置恢复。操作方式如下:

在仍然存活且数据最完整的节点上:

SET GLOBAL group_replication_force_members="172.16.20.21:33061,172.16.20.22:33061";

这个命令会把组强制收缩到指定的两个节点。使用前一定要确认没有被强制切除的其他节点,否则等它们恢复后再连进来,可能出现脑裂和重复主节点。我自己的规矩是:强制恢复这条命令,必须在业务停写、并且和团队确认过数据完整性后才能执行。

另外我在配置里把 group_replication_exit_state_action 设置成了 READ_ONLY,而不是默认的 ABORT_SERVER。这个参数的含义是:当节点因为意外原因退出组时,实例是变成只读还是直接 shutdown。生产环境我建议用 READ_ONLY,这样人还在,MySQL 进程还在,问题诊断和排查要容易得多。如果设成 ABORT_SERVER,节点一退出组就直接把数据库进程关掉,反而会让业务慌。

5. 问题排查速查表

为了方便翻阅,我把搭建过程里最容易出现的问题整理成了表格,按现象、原因、处理方式三列排好:

现象可能原因处理办法
客户端报 ERROR 2002 socket 连不上socket 路径不一致用 -S 指定实际 socket,或统一 [client] 配置
INSTALL PLUGIN 找不到 group_replication.so安装包不完整/版本过旧确认使用官方 8.4.7 RPM 或二进制包
START GROUP_REPLICATION 一直在 RECOVERINGrecovery 通道 SSL/权限/binlog 不满足检查 SSL 参数、账号权限、binlog 保留时间
START GROUP_REPLICATION 报 ERROR 3096第一节点没开 bootstrap首个节点 SET GLOBAL group_replication_bootstrap_group=ON 后再启动
成员退出后整个组只读节点数不足多数派保持三节点或恢复节点;必要时 force_members
多主模式数据冲突单双主配置不一致检查 single_primary_mode 和 enforce_update_everywhere_checks
节点自动重启后状态异常start_on_boot 行为影响生产环境建议保留 OFF,手动控制加入时机
复制用户连接时公钥检索失败caching_sha2_password 未走 SSL开启 recovery_use_ssl,或临时开 get_public_key
备库查询不到最新数据应用没走主节点单主模式下备库是只读,读写入口必须指向 PRIMARY

6. 容器和 Kubernetes 环境中落地组复制的注意点

6.1 Docker 里跑 MGR 的坑

有人会在 Docker 里部署 MySQL 8.4.7 来快速验证组复制。思路没问题,但有几个坑建议提前避开。

第一,容器的 IP 不能随便漂移。MGR 的 group_replication_local_address 和 group_seeds 都依赖具体 IP,容器一旦重建,IP 变了,节点就找不回来了。我用的办法是建一个固定的 docker 网络,给每个容器指定固定 IP,并把数据目录挂到宿主机:

docker network create --subnet=172.18.0.0/24 mgr-net docker run -d \ --name mysql-mgr-1 \ --network mgr-net \ --ip 172.18.0.21 \ -e MYSQL_ROOT_PASSWORD='Root_Pass_2024' \ -v /data/mysql1:/var/lib/mysql \ mysql:8.4.7 \ --server-id=1 \ --gtid-mode=ON \ ...

容器内部的 local_address 必须写成容器自己的固定 IP,不要写 localhost 或容器名。因为组内节点之间要通过这个地址互相通信,写成 localhost 就只有容器内部能连。

第二,容器重启后 mysqld 是否自动启动组复制,取决于 start_on_boot 参数。容器环境下创建新容器后,配置会被重新读取,我强烈建议在容器启动完成后手工执行 START GROUP_REPLICATION,用健康检查脚本辅助判断,而不是依赖自动归位。

6.2 Kubernetes / KubeSphere 环境的主从部署

在生产 Kubernetes 环境里搭 MGR,比 Docker 又难一个维度。Kubernetes 的 Pod 天然是“临时资产”,IP 不固定,节点重启会换地址,Pod 漂移会让 MGR 的成员列表彻底失效。目前最好的实践是:

  • 使用 StatefulSet 部署,保证每个 Pod 拥有稳定的网络标识,比如 mysql-0.mysql-mgr.namespace.svc.cluster.local。
  • group_replication_local_address 用 Pod 的域名而非 IP,group_replication_group_seeds 写成三个 Pod 的完整域名列表。
  • 使用 headless Service 配合 ClusterIP 访问,让业务连接保持稳定。
  • 使用持久化卷保存 /var/lib/mysql,避免 Pod 重建后数据丢失。

KubeSphere 的应用商店里有很多 MySQL 相关 Helm Chart,但多数是传统主从或单实例,不一定是 MGR。如果你在 KubeSphere 里点了“部署 MySQL”,先看部署模板里有没有 group_replication_* 相关参数,没有的话,说明它只是“有主从关系的普通复制”,不是组复制。真想在企业容器平台跑 MGR,要么自己按 StatefulSet 写模板,要么直接上 MySQL Operator,让 Operator 管理 MGR/InnoDB Cluster 生命周期,省得自己天天盯成员状态。

在容器环境里尤其要注意健康检查的探针写法。MGR 节点不是所有状态都能对外提供写服务,比如 RECOVERING 期间还不应该接业务流量,探针里不能只检查 TCP 3306 是否通,更稳妥的方式是执行:

SELECT member_state FROM performance_schema.replication_group_members WHERE member_host = @@hostname;

只有返回 ONLINE 时才代表节点真正可用。

回到最开始的问题:8.4.7 上做“主从搭建”,组复制是性价比很高的方案。但 MGR 不是那种“配好参数就躺着不管”的东西,节点退出、网络分区、恢复顺序都需要规范和演练。我个人在部署完这套三节点后,把自动加入、退出动作、强制恢复流程都沉淀成了文档,宁可一开始麻烦一点,也比半夜收到告警后再手忙脚乱好得多。如果你正准备搭,记得先把业务表主键规范、防火墙端口、复制账号权限这三件事提前确认,剩下的大半问题基本都能避免。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询