☰
Ubuntu 24下MySQL 8.0.36 MGR组复制部署实战
2026/10/15 5:11:53 网站建设 项目流程

最近我在 Ubuntu 24 上重新搭了一套 MySQL 8.0.36 的 MGR(MySQL Group Replication),整个过程踩了几个不常见的坑。网上很多教程还停留在 MySQL 5.7 时代,参数名和权限要求已经变了,照着抄大概率会在START GROUP_REPLICATION这一步卡住。这篇我就用纯手工配置的方式,把从安装、基础环境、三节点配置到第一个节点引导、后续节点加入、常见排障的完整链路重新走一遍。内容更适合刚准备上 MGR 的运维和 DBA,也适合开发同学理解组复制到底是怎么工作的。

1. 读懂 MGR:它不是普通主从复制,而是“组内共识 + 写集认证”

1.1 MGR 到底解决了什么问题

MGR 不是简单的多主复制,也不是把传统主从复制做了几个节点拼凑。它本质上是一个内置于 MySQL 的插件,通过组通信系统在不同节点之间同步事务,使用类似 Paxos 的共识协议来保证成员之间对事务顺序的认知一致。

这里最关键的一点是:MGR 是“数据副本 + 共识”的一体化方案,而不是异步主从那种“主库只负责生产日志,从库自己决定拉到哪”。

传统异步主从最大的问题就是延迟不可控:主库 binlog 已经写了,从库可能还差几秒,主库一旦宕机,你根本不知道从库丢了多少数据。半同步复制虽然好一些,但遇到主库直接断电的场景还是存在“主从都以为自己是最新”的脑裂风险。而 MGR 每个节点在提交事务时,都要经过组通信层的广播和确认,多数派节点接受后事务才最终生效,这就从机制上消除了数据丢失的那段窗口。

1.2 单主模式和多主模式,我建议默认选单主

MGR 支持单主模式和双主模式(多主模式):

  • 单主模式:整个组只有一个主节点可以写,其他节点自动变成只读。主节点故障后,组内自动选举一个新的主节点,应用层只需要感知到主节点地址变化即可。
  • 多主模式:所有节点都可以写,事务会进行“写集冲突检测”,冲突事务会被回滚。

我在实际项目里几乎只用单主模式。原因很简单:多主模式引入的冲突回滚对应用层是极大的负担,尤其是那些没做幂等设计的业务,一个回滚就可能导致订单状态或余额数据对不上。单主模式的自动故障转移已经很成熟,又不是非要在所有节点上写才行。只有在某些特定场景,比如分片多活、各机房独立写入后才考虑多主。

1.3 先掂量下要想跑顺畅,哪些前提条件是硬性的

MGR 对部署环境有硬性要求,不满足的话,后面字面意义的“起不来”:

  1. 至少三台节点(推荐奇数个),两台节点在单节点故障时无法形成多数派,整套集群会进入阻塞状态。
  2. 每台节点时间要同步,建议安装 chrony 或 NTP 服务,时间偏差过大会导致组通信超时。
  3. InnoDB 是唯一被允许的存储引擎,MyISAM、BLACKHOLE 等要给禁掉,尤其是 MyISAM,在 MGR 里会被直接拒绝。
  4. 所有表都需要有主键,否则写集冲突检测会出问题。实际上,MGR 在启动时可能不会立刻报错,但运行时数据同步遇到无主键表就非常麻烦。
  5. 节点之间的网络要稳定,3306 端口用于 MySQL 客户端连接,33061 端口用于组通信,防火墙要提前放通。
  6. 低版本 MySQL 对 MGR 限制较多,8.0.36 已经完全够用,事务写集提取算法默认使用 XXHASH64。

这些条件看起来简单,但搭建过程中如果出现莫名超时,大概率是防火墙或时间同步的问题。先把它们处理干净,再进入下面的安装。

2. Ubuntu 24 环境准备,以及如何锁定安装 MySQL 8.0.36

2.1 三节点网络规划与系统初始化

我用三台节点来做演示,IP 分别规划为 10.10.10.11、10.10.10.12、10.10.10.13。为了方便识别,我会先修改主机名和 /etc/hosts 文件。

# node1 上执行 hostnamectl set-hostname node1 # node2 上执行 hostnamectl set-hostname node2 # node3 上执行 hostnamectl set-hostname node3

然后三台节点都写入一致的 hosts 映射:

cat >> /etc/hosts <<EOF 10.10.10.11 node1 10.10.10.12 node2 10.10.10.13 node3 EOF

这不是必须步骤,但强烈建议做。原因有两个:一是组复制的group_replication_group_seeds里如果直接用 IP,那当然没问题;但如果后续改成域名,hosts 没做全就会踩“无法解析成员地址”的坑。二是故障排查时,日志里如果只显示 IP,很难一眼看出是哪台节点。

接着装时间同步。MySQL 组复制对节点间的时钟偏移容忍度比较低,我习惯统一用 chrony,安装方式和传统 NTP 几乎一样:

sudo apt update sudo apt install -y chrony sudo systemctl enable --now chrony

装好后可以用chronyc tracking看系统与时间源的偏差。这里有个容易被忽略的点:如果是云服务器或虚拟机,宿主的时钟漂移可能比想象的大,尤其是那些开过挂起和快照恢复的机器,时间会跳得很厉害。我测试时遇到过成员之间相差几秒,导致组通信一直报超时,后来把三台节点的 chrony 都配好,重启了 mysqld 才恢复。

2.2 安装 MySQL 8.0.36:系统源和官方源的取舍

Ubuntu 24.04 的默认源里就有 MySQL 8.0 系列,如果只是要跑 MGR,直接装系统源版本是最省事的:

sudo apt update sudo apt install -y mysql-server mysql --version

执行后应该能看到类似mysql Ver 8.0.36 for Linux on x86_64的输出版本信息。如果系统源已经更新到更高版本,可以通过apt-cache madison mysql-server找到可安装的 8.0.36 版本号,再按显式版本号安装。

假如你想严格锁定 8.0.36,还要注意一点:apt 升级时可能会把 mysql-server 升到其他小版本。对组复制来说,同组内版本差异过大会有兼容风险,所以我建议用 apt-mark 固定:

sudo apt-mark hold mysql-server

与 Ubuntu 源相比,手动安装 MySQL 官方 apt 仓库的优势是版本选择更灵活,但代价是仓库里软件包的命名和默认目录会有点区别,比如会出现mysql-community-server这类包名称,配置文件也会放在/etc/mysql/mysql-community.conf.d/。追求稳定和可复现的话,Ubuntu 源 + apt-mark hold 反而是更轻量的选择。

2.3 安装后的安全加固和基础参数确认

装完 MySQL,我先做一次基础安全设置:

sudo mysql_secure_installation

运行后会让你选择密码强度等选项。建议 root 使用 caching_sha2_password 认证方式,这是 MySQL 8.0 的默认认证,不要为了兼容旧应用而改成 mysql_native_password,后面 MGR 的分布式恢复账号也不会因此有障碍。

加固完再检查几个默认参数:

mysql -uroot -p -e "SHOW VARIABLES LIKE 'server_id';" mysql -uroot -p -e "SHOW VARIABLES LIKE 'binlog_format';" mysql -uroot -p -e "SHOW VARIABLES LIKE 'gtid_mode';"

如果gtid_mode还是 OFF,也不用急,我们后面会在配置文件里统一打开。现在只是确认 MySQL 能正常启动、root 能登录,环境基础就算准备好了。

这一步很多人会跳过,但我不建议。等所有配置做完再一次性重启,遇到问题很难排查到底是哪一步出错。先确认 MySQL 服务健康的“地基”,再动 MGR 的配置文件,符合“一次只引入一个变量”的排错原则。

3. 三节点配置文件:逐项拆解 MGR 必须开启的参数

3.1 MGR 对 GTID、binlog 和存储引擎的硬性要求

MGR 本质上依赖 GTID 和 binlog 实现分布式恢复。所以每个节点的配置里,GTID 和 binlog 相关参数必须全部按规范设置。我习惯把 MGR 的独立配置单独放到/etc/mysql/mysql.conf.d/mgr.cnf,不直接改主配置文件,这样以后做参数回滚比较方便。

在 node1 上创建文件:

[mysqld] # 基础复制与 GTID server_id = 1 gtid_mode = ON enforce_gtid_consistency = ON log_bin = mysql-bin log_slave_updates = ON binlog_format = ROW binlog_checksum = NONE # 写集提取 transaction_write_set_extraction = XXHASH64 # 禁用非 InnoDB 引擎 disabled_storage_engines = "MyISAM,BLACKHOLE,EXAMPLE,FEDERATE,ARCHIVE"

这里逐个解释,避免大家只抄参数不思考:

  • gtid_mode = ON:开启全局事务标识,MGR 内部用它来定位每一个事务的位置。
  • enforce_gtid_consistency = ON:禁止执行会导致 GTID 不一致的语句,比如创建临时表时跨事务更新等操作。
  • log_bin:打开二进制日志,MGR 分布式恢复和事务认证都依赖它。
  • log_slave_updates = ON:让中继日志重放的事务也写入本机 binlog。在传统主从中,这个参数控制 relay log 是否记录到本地 binlog,在 MGR 中它同样是每个节点都能留存完整 binlog 的前提。
  • binlog_format = ROW:行级复制。MGR 写集冲突检测要求使用 ROW 格式。
  • binlog_checksum = NONE:这一项与默认配置不同,默认可能是 CRC32。MGR 组通信层有自己的数据校验,如果在 binlog 层再做 CRC32 校验,传输和恢复时容易出现格式不匹配的问题。官方文档明确要求 MGR 环境必须设置成 NONE。

disabled_storage_engines是可选但建议配置的。MGR 只支持 InnoDB,如果有人误建了 MyISAM 表,启动组复制或数据同步时会报错。手动禁用 MyISAM,相当于让 MySQL 从源头拒绝这些表。

3.2 组复制专属参数

上面只是复制的“通用底座”,真正让实例变成 MGR 成员的是下面这一段:

[mysqld] # 组复制专属配置 group_replication = ON group_replication_group_name = "24505d7f-9b8e-4d2e-8b5f-1c3e48cc9a31" group_replication_start_on_boot = OFF group_replication_local_address = "10.10.10.11:33061" group_replication_group_seeds = "10.10.10.11:33061,10.10.10.12:33061,10.10.10.13:33061" group_replication_bootstrap_group = OFF group_replication_single_primary_mode = ON group_replication_enforce_update_everywhere_checks = OFF group_replication_ip_allowlist = "10.10.10.11,10.10.10.12,10.10.10.13"

参数说明如下:

  • group_replication_group_name:是一个 UUID,代表整个组的唯一名称。你可以用uuidgen生成一个,三台节点必须一样。
  • group_replication_start_on_boot:默认关闭。千万不要轻易打开。如果打开,任何一台机器重启后都可能尝试加入组,在没有经过 bootstrap 的初始场景下,会出现成员状态混乱。我更建议在服务层面或手动管理层面控制启动时机。
  • group_replication_local_address:本节点用于组通信的地址和端口,端口通常不跟 3306 冲突,取 33061 是默认习惯。
  • group_replication_group_seeds:三台节点组通信地址的种子列表。新成员启动时,会尝试连接这些种子中的在线成员来获取组信息。
  • group_replication_bootstrap_group:同样默认关闭,只在第一个节点初始化时手动打开。这个参数的作用是告诉新组“这里有第一个成员”,如果保持默认,第一个节点永远无法建立组。
  • group_replication_single_primary_mode:单主模式开启。
  • group_replication_enforce_update_everywhere_checks:多主模式时使用,单主模式下保持 OFF。
  • group_replication_ip_allowlist:组通信白名单。MySQL 8.0.22 之后叫 allowlist,老参数ip_whitelist已经被替代。这里要小心,如果填错了 IP,节点之间将无法建立组通信。

3.3 三台节点之间差异在哪

同一份配置,node2 和 node3 只需要改两个地方:

node2 的差异化配置:

server_id = 2 group_replication_local_address = "10.10.10.12:33061"

node3 的差异化配置:

server_id = 3 group_replication_local_address = "10.10.10.13:33061"

其余参数比如group_replication_group_name、group_replication_group_seeds必须一字不差地保持一致。这里容易踩的坑是:有人把group_replication_group_seeds写成本机地址加一个逗号,拼写错误,结果后续节点加入时一直找不到组。

配置完成后,分别在三个节点上重启 mysql:

sudo systemctl restart mysql sudo systemctl status mysql

确认 MySQL 都能正常启动。如果启动失败,用下面的命令看错误日志:

sudo tail -100 /var/log/mysql/error.log

不要带着错误往下走,MGR 要求所有节点在参数层面完全一致,早发现早处理。

4. 第一个节点引导:把分布式恢复账号和组初始化一次做对

4.1 登录 MySQL 并创建用于组恢复的复制账号

第一步,登录 node1 的 MySQL:

mysql -uroot -p

MGR 的成员加入需要通过group_replication_recovery通道进行分布式恢复,而这个恢复过程需要一个复制账号。这个账号在每个节点上都要创建,并且权限要足够。

我建议创建一个名叫 repl 的账号,权限包含复制、备份克隆和管理组复制相关的权限:

CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl_Strong_Password_2025'; GRANT REPLICATION SLAVE, REPLICATION CLIENT, BACKUP_ADMIN, GROUP_REPLICATION_ADMIN, CLONE_ADMIN ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;

这里特别提一下BACKUP_ADMIN和CLONE_ADMIN。MGR 在成员加入时,如果发现新成员缺失的 binlog 已经不存在,会自动通过 clone 方式从在线成员拉取全量数据。如果复制账号权限不足,这一步会直接失败。所以虽然看起来只是建一个复制账号,实际上要把克隆需要的权限一并给到位。

另外还需要为分布式恢复通道设置连接账号:

CHANGE REPLICATION SOURCE TO SOURCE_USER='repl', SOURCE_PASSWORD='Repl_Strong_Password_2025' FOR CHANNEL 'group_replication_recovery';

如果你更习惯旧版本语法,等价写法是:

CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='Repl_Strong_Password_2025' FOR CHANNEL 'group_replication_recovery';

MySQL 8.0.36 里旧语法仍然可用,但新语法是未来方向,我建议用新的。

4.2 先建一个测试库,验证复制是否真的有效

在上线真实业务库之前,最好先在主库建一个测试库并写入一些数据。我用它来验证后续节点是否真的能同步,而不仅是“看起来加入了组”。

CREATE DATABASE testdb; USE testdb; CREATE TABLE t1 (id INT PRIMARY KEY, data VARCHAR(100)); INSERT INTO t1 VALUES (1, 'hello mgr');

等三个节点都加进来之后,再看这张表的数据是不是同步了。

4.3 首次启动组的 bootstrap 顺序:只有这次能开

现在到最关键的时刻。先在 node1 上把 bootstrap 参数临时打开:

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

为什么必须手动执行这三条?因为group_replication_bootstrap_group如果常驻 ON,等于允许任何节点随时创建一个新组。这会造成脑裂,生产环境里绝对不能让它开着。

START GROUP_REPLICATION;执行后,可以马上检查成员状态:

SELECT * FROM performance_schema.replication_group_members\G

正常情况下会看到一条记录,MEMBER_STATE是ONLINE。如果看到OFFLINE,查看错误日志。常见的报错原因包括:

  • 复制账号权限不全。
  • group_replication_group_seeds填写错误。
  • IP 白名单没有包含本机或对方。
  • 时间同步差太多。

此时 node1 已经是一个单节点的组,组内还没其他成员。接下来要把 node2 和 node3 拉进来。

5. 第二、第三个节点如何加入:理解分布式恢复的两种路径

5.1 添加 node2 的关键操作

在 node2 上先登录 MySQL,做和 node1 相同的账号创建和通道配置:

CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl_Strong_Password_2025'; GRANT REPLICATION SLAVE, REPLICATION CLIENT, BACKUP_ADMIN, GROUP_REPLICATION_ADMIN, CLONE_ADMIN ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES; CHANGE REPLICATION SOURCE TO SOURCE_USER='repl', SOURCE_PASSWORD='Repl_Strong_Password_2025' FOR CHANNEL 'group_replication_recovery';

然后执行:

START GROUP_REPLICATION;

注意这里一定不要执行SET GLOBAL group_replication_bootstrap_group = ON。node2 的角色是加入现有组,而不是创建新组。

执行完,回到 node1 上查看:

SELECT * FROM performance_schema.replication_group_members\G

你会看到 node1 和 node2 都处于ONLINE状态。如果 node2 显示RECOVERING,表示它正在从某个在线成员拉取历史数据或进行 clone。数据量小的话几秒钟就变成 ONLINE,数据量大或全量 clone 时可能需要几分钟。

在添加 node2 时,最容易遇到的问题就是“分布式恢复找不到合适的 donor”。发生这种错误时,先不要反复重试,去备机上查看错误日志,看是不是 binlog 已经清理、账号权限不全,或者节点间无法通过 33061 端口通信。

5.2 添加 node3:操作流程和 node2 一样,但有版本差异坑

node3 的步骤和 node2 一模一样,同样是先建账号、建通道,再START GROUP_REPLICATION。跑完后,三节点组的最终状态应该是:

  • node1:ONLINE
  • node2:ONLINE
  • node3:ONLINE

这里要提醒的是,如果三台节点安装的 MySQL 小版本号不一致,会出现“版本不可兼容”的问题。MGR 对组内版本一致性有校验,虽然是 8.0.36 系列,但小版本不同也可能导致加入失败。我用 apt-mark hold 锁版本,就是为了避免这种问题。

另外,初学者容易犯的一个错误是:先给 node2 启动,再给 node3 启动,但 node3 配置文件的group_replication_group_seeds漏了 node2 的地址。虽然理论上只要有一个在线成员的地址就可以,但三台都写在 seeds 里更稳妥,因为当某一台节点故障时,新节点仍然能通过其他节点找到组。

5.3 用 performance_schema 验证成员状态与延迟

验证 MGR 是否健康,不能只看三个节点是不是 ONLINE,还要看各个节点的事务应用情况。

SELECT * FROM performance_schema.replication_group_member_stats\G

这个表里有两个很关键的字段:

  • COUNT_TRANSACTIONS_IN_QUEUE:等待应用的事务数,正常情况下应该是 0。
  • COUNT_TRANSACTIONS_CONFLICTS:冲突事务数,单主模式下应该长期为 0。

还可以用SHOW STATUS LIKE 'group_replication_primary_member';查看当前哪个节点是主节点:

SHOW STATUS LIKE 'group_replication_primary_member';

输出会是一个 server_uuid 对应字符串,对应到具体节点后,可以确认当前主节点就是业务写入目标。如果应用层只把连接固定到某一台节点上,后续主节点切换后就会出现“没有写主库”的问题。这个一定要提前规划。

数据同步验证也很简单,在 node2 和 node3 上查看之前建的表:

SELECT * FROM testdb.t1;

如果能看到 node1 插入的那行数据,说明组复制已经真正把数据同步过去了。

6. 数据同步验证与故障处理:日常最容易踩的坑

6.1 日常巡检该看哪些东西

MGR 搭建完,不代表可以放着不管。我日常巡检基本只看四个维度:

第一,成员状态:

SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;

成员状态必须是 ONLINE,角色出现 PRIMARY 和 SECONDARY 才是正常的。

第二,事务堆积:

SELECT MEMBER_ID, COUNT_TRANSACTIONS_IN_QUEUE, COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE FROM performance_schema.replication_group_member_stats;

队列长期不为零,说明节点性能跟不上或存在阻塞。

第三,主库确认:

SHOW STATUS LIKE 'group_replication_primary_member';

第四,错误日志。建议配置 log_error 并定期巡检,别等集群挂了才想到看。

6.2 成员掉线后的恢复流程

MGR 最让人头疼的是节点掉线,尤其是状态变为 ERROR 后,很多人会慌。我先说判断流程,再说具体怎么处理。

当某个节点的MEMBER_STATE变成ERROR或UNREACHABLE时,先按顺序排查:

  1. 检查该节点数据库是否还在运行,systemctl status mysql。
  2. 检查网络和防火墙是否拦截了 33061 端口。
  3. 检查系统时间是否出现较大偏差,chronyc tracking。
  4. 最后看错误日志,确认是否有磁盘满、连接数超限等资源问题。

如果只是临时网络抖动导致节点被组移除,还可以在节点上直接重启组复制:

STOP GROUP_REPLICATION; START GROUP_REPLICATION;

只要基础数据没有丢,节点会重新执行分布式恢复,从在线成员那里补齐遗漏的事务。如果节点已经严重落后,binlog 被清理,则会自动走 clone 全量恢复。这种情况下恢复时间取决于数据量大小。

这里有个非常关键的禁忌:当组内多数成员已经挂掉、只剩下一个 ONLINE 节点时,千万不要在这个节点上直接执行:

SET GLOBAL group_replication_bootstrap_group = ON; START GROUP_REPLICATION;

除非你已经明确知道这唯一节点包含最新数据,并且愿意承担可能丢失其他节点已确认事务的风险。否则很可能会把一个不完整的数据副本强行变成“新组”,等旧节点恢复后造成数据不一致。正确做法是先把故障节点恢复,尽量让原组成员重新聚齐,再决定是否需要人工介入。

6.3 应用层连接方案和后续演进

MGR 本身只管数据同步和自动选主,它不会自动把应用连接从旧主节点切换到新主节点。这就是为什么接着要引入 MySQL Router 或 VIP 的原因。我给的配置中,节点地址是 10.10.10.11 等,应用层如果只写死主库 IP,主从切换后应用就会断连。

生产环境常见的做法有几种:

  • MySQL Router 的读写分离和自动路由,把流量引导到当前主节点。
  • LVS / Keepalived 做 VIP,主节点故障时动态迁移 IP。
  • 应用层自己做节点状态探测,这通常只适合内部运维系统。

我更推荐 MySQL Router,因为它对 MGR 的主节点探测做得更原生,还能控制读写分离,配合REPLICATION GROUP MEMBERS的上报信息,让应用始终连接正确的主库。

另外,MGR 所在的节点都要避免直连 root 对外网暴露。复制账号的密码建议用密码管理器定期轮换,别把明文密码写在命令历史里。分布式恢复通道如果更换密码,记得在所有节点上重新执行CHANGE REPLICATION SOURCE TO,并重启组复制。

6.4 给刚入门的人三个最直接的建议

如果这是你第一次搭 MGR,先照着本文在三台测试机上跑通,不要一上来就改 100 个参数。等集群稳定运行几天后,再做一次主节点切换演练,感受一下故障转移的实际行为。这样才能避免“文档上看着挺稳,实际一挂就手足无措”。

另外,生产环境记得把group_replication_start_on_boot保持 OFF,把启动组复制的动作放到编排脚本里,配合 MySQL Router 的探测,让服务具备可控的启动顺序。别图省事,不然节点重启后可能以错误角色加入组,那才是大麻烦。

最后再分享一个小技巧:在配置完成和启动组复制之间,我会特意执行一次SHOW VARIABLES LIKE 'group_replication_%';,把所有参数打印出来后,和三台节点的配置逐项核对。这个习惯帮我发现过好几次由于复制粘贴漏参数导致的低级错误。MGR 的关键就是“细节一致”,少一个NONE、多一个空格,都可能让整个集群状态变得不可预测。

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

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

立即咨询