Ubuntu 20.04 + MySQL 8.0 构建三节点MGR高可用集群实战
1. 项目概述:为什么在 Ubuntu 20.04 上配置 MySQL 组复制不是“选修课”,而是生产环境的必修动作
MySQL Group Replication(MGR)不是个新名词,但直到 Ubuntu 20.04 成为长期支持(LTS)主力发行版,配合 MySQL 8.0.13+ 的稳定成熟,它才真正从“实验室玩具”蜕变为可落地的高可用基石。我接手过三个线上系统——一个电商订单中心、一个金融风控日志平台、一个教育类 SaaS 的用户行为分析库,它们最初都用主从复制扛流量,结果无一例外在某次主库磁盘满、网络抖动或运维误操作后,出现数小时级的读写中断、数据不一致甚至脑裂。后来全部迁移到 MGR 架构,故障恢复时间从小时级压缩到秒级,且整个过程无需人工介入切换。这不是玄学,是基于 Paxos 协议的分布式共识机制在数据库层的真实兑现。Ubuntu 20.04 提供了干净、统一、长期受支持的运行时环境,内核 5.4、systemd 245、OpenSSL 1.1.1f 等组件版本与 MySQL 8.0 官方二进制包深度适配,避免了旧版 Ubuntu 中常见的 glibc 兼容性问题和 systemd 服务管理冲突。你可能看到热搜里全是“mysql安装教程”“ubuntu没声音20.04”,但真正决定系统生死的,从来不是那些表层功能,而是底层数据一致性保障能力。这个项目标题直指核心:它不是教你如何把 MySQL 装上系统,而是教你如何让三台(或更多) Ubuntu 20.04 服务器上的 MySQL 实例,像一个有机体一样协同工作、自动选举、故障自愈。适合谁?不是刚装完 MySQL 就想跑 SELECT * FROM users 的新手,而是已经经历过主从同步延迟报警、GTID 丢失、从库跳错位等痛楚的 DBA、SRE 或全栈工程师。如果你正为单点故障提心吊胆,或被业务方追问“数据库挂了多久能恢复”,那么接下来的内容,就是你该抄的第一份作业。
2. 整体设计与思路拆解:为什么放弃传统主从,坚定选择 MGR 的三重逻辑
2.1 架构选型不是技术炫技,而是对业务连续性的精准响应
很多人配置 MGR 是因为“听说它很新”,这非常危险。我见过团队花两周搭好三节点 MGR,结果上线后发现应用连接池没做读写分离,所有请求打到同一节点,其他两个节点纯属摆设;也见过因防火墙规则漏掉 group_replication_local_address 端口,集群永远无法形成。所以第一步必须回归本质:我们到底要解决什么问题?答案很朴素——消除单点故障,实现自动故障转移,保证数据强一致性。传统异步主从复制(Async Replication)有三个硬伤:一是主库宕机后,从库可能丢失最后几秒事务(异步特性决定);二是故障转移需人工干预或依赖外部工具(如 MHA),存在操作窗口期;三是多写场景下极易产生冲突(比如两个应用同时向不同从库写入同一条记录)。而半同步复制(Semi-sync)虽能缓解第一点,但无法解决后两点。MGR 则从根本上重构了逻辑:它不区分“主”和“从”,所有节点都是对等的(peer-to-peer),每个写入事务必须经过集群多数节点(quorum)确认才能提交,天然满足 CAP 理论中的 CP(一致性+分区容忍性)。这意味着,只要集群中存活节点数 ≥ ⌊N/2⌋+1(N 为总节点数),系统就始终可用且数据不丢。对于三节点集群,允许 1 台宕机;五节点则允许 2 台宕机。这种数学确定性,是任何脚本或中间件都无法提供的底层保障。
2.2 Ubuntu 20.04 与 MySQL 8.0 的组合,是当前最稳的生产基线
选择 Ubuntu 20.04 而非 18.04 或 22.04,并非偶然。18.04 的 MySQL 默认源只提供 5.7 版本,而 MGR 是 MySQL 5.7.17 才引入的实验性功能,到 8.0.13 才正式 GA(General Availability),稳定性、性能和文档完备度不可同日而语。22.04 虽然自带 MySQL 8.0.28+,但其 systemd 249+ 对 MySQL 服务单元文件(mysqld.service)的依赖解析更严格,曾导致我们一个客户在升级后 mysqld 启动失败,排查耗时半天。Ubuntu 20.04 的平衡点恰到好处:它通过官方 APT 源(http://archive.ubuntu.com/ubuntu focal-updates/main)稳定提供 MySQL 8.0.25(这是 2021 年发布的关键 LTS 版本),该版本修复了早期 8.0.x 中 group_replication_recovery_retry_count 参数失效、集群状态机卡死等致命 Bug。更重要的是,20.04 的内核 5.4 对 NUMA 架构支持更完善,在多路 CPU 服务器上能显著降低 MGR 心跳包(heartbeat)的延迟抖动,这对 Paxos 投票超时(group_replication_member_expel_timeout)的稳定性至关重要。我们实测过:在相同硬件上,Ubuntu 20.04 + MySQL 8.0.25 的集群平均投票延迟为 12ms,而 18.04 + 8.0.13 则高达 47ms,后者在高负载下极易触发误驱逐(expel)。
2.3 三节点最小集群的设计哲学:成本、复杂度与可靠性的黄金三角
MGR 官方文档建议至少三节点,但我们坚持“三节点是生产起步的绝对底线”,理由很实在。首先,两节点集群(2-node)在 MGR 中是伪高可用:当一台宕机,剩余一台无法构成多数派(quorum=2,需要 ≥2 节点同意),集群会整体只读甚至停止服务,这比单点更糟。其次,五节点虽容灾能力更强,但带来三重负担:一是网络开销翻倍(Paxos 消息广播量随节点数平方增长);二是运维复杂度指数上升(配置项、状态监控、故障排查维度成倍增加);三是硬件成本直接提升 60%。我们做过压测:在 10Gbps 网络下,三节点集群处理 5000 TPS 写入时,网络带宽占用率仅 12%;而五节点同等负载下,带宽占用率达 38%,且心跳包丢包率上升 3 倍。因此,三节点不是妥协,而是经过成本、性能、可靠性三维权衡后的最优解。它用最低的硬件投入,换取了生产环境必需的“单节点故障零感知”能力。后续扩展为五节点,应是业务规模翻倍、数据量激增后的自然演进,而非初始设计。
3. 核心细节解析与实操要点:绕不开的七个生死关卡
3.1 网络层:别让防火墙和 DNS 成为集群的“隐形杀手”
MGR 集群通信依赖两个独立端口:一个是 MySQL 服务端口(默认 3306),用于客户端连接和 SQL 流量;另一个是组复制专用端口(group_replication_local_address),用于节点间 Paxos 协议通信、心跳、状态同步。很多失败案例源于此。Ubuntu 20.04 默认启用 ufw(Uncomplicated Firewall),若未显式放行组复制端口,节点将永远无法握手。假设你规划节点 A(192.168.1.10)、B(192.168.1.11)、C(192.168.1.12),并为组复制分配 33061 端口,则必须在每台机器执行:
sudo ufw allow from 192.168.1.10 to any port 33061 sudo ufw allow from 192.168.1.11 to any port 33061 sudo ufw allow from 192.168.1.12 to any port 33061 sudo ufw reload提示:切勿使用
sudo ufw allow 33061开放所有来源,这会暴露集群内部协议,存在安全风险。必须精确到源 IP。
DNS 解析是另一大陷阱。MGR 要求所有节点能通过 hostname 相互解析(例如 node1.internal、node2.internal),但 Ubuntu 20.04 的 /etc/hosts 文件默认为空,且 systemd-resolved 服务可能干扰本地解析。我们的做法是彻底绕过 DNS,强制使用 IP。在 my.cnf 中,group_replication_local_address 必须写成192.168.1.10:33061,而非node1.internal:33061;同时,group_replication_group_seeds 必须列出所有节点的 IP:PORT,如"192.168.1.10:33061,192.168.1.11:33061,192.168.1.12:33061"。实测证明,IP 直连比 DNS 解析快 8-12ms,且杜绝了因 /etc/resolv.conf 配置错误导致的集群启动失败。
3.2 MySQL 配置:my.cnf 中那 12 行决定集群生死
MGR 对 MySQL 配置有严苛要求,少一行或多一行都可能导致集群无法启动或状态异常。以下是三节点集群中,每台机器 my.cnf [mysqld] 段落必须包含的 12 个核心参数(已按逻辑分组):
| 参数类别 | 参数名 | 推荐值 | 关键原因 |
|---|---|---|---|
| 基础标识 | server_id | 1001 (A), 1002 (B), 1003 (C) | 必须全局唯一,且不能为 1(MySQL 8.0 默认值,易冲突) |
| GTID 强制 | gtid_mode | ON | MGR 依赖 GTID 追踪事务,禁用则报错 |
| enforce_gtid_consistency | ON | 确保所有事务兼容 GTID,否则 CREATE TABLE ... SELECT 会失败 | |
| 二进制日志 | binlog_format | ROW | MGR 仅支持行格式,STATEMENT 或 MIXED 会导致复制中断 |
| log_bin | /var/log/mysql/mysql-bin.log | 必须开启,MGR 从 binlog 读取事务事件 | |
| binlog_checksum | NONE | MySQL 8.0.20+ 默认 CRC32,但某些旧版客户端不兼容,设为 NONE 避免校验失败 | |
| 组复制开关 | plugin_load_add | 'group_replication.so' | 动态加载插件,不加则插件无法启用 |
| transaction_write_set_extraction | XXHASH64 | 生成事务写集(write set)的哈希算法,XXHASH64 性能最优 | |
| 组通信 | group_replication_group_name | "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" | UUID 格式,三节点必须完全一致,建议用 uuidgen 生成 |
| group_replication_local_address | "192.168.1.10:33061" | 本节点监听地址,IP 和端口必须与防火墙规则匹配 | |
| group_replication_group_seeds | "192.168.1.10:33061,192.168.1.11:33061,192.168.1.12:33061" | 种子节点列表,新节点加入时从此获取集群视图 | |
| group_replication_bootstrap_group | OFF | 仅在初始化第一个节点时临时设为 ON,其余节点必须为 OFF |
注意:
group_replication_bootstrap_group=ON是“雷区”。它只能在集群首次创建时,由第一个节点执行一次,且执行后必须立即设回 OFF。若第二个节点也设为 ON,会导致两个独立集群诞生,数据彻底分裂。我们封装了一个安全脚本:bootstrap-first-node.sh,它会自动检查 performance_schema.replication_group_members 表,确认无其他成员后再执行 START GROUP_REPLICATION。
3.3 用户与权限:那个被忽略的 replication_applier 账号
MGR 要求一个专用的复制用户(replication user),但很多人只创建了用于主从复制的用户,却忘了 MGR 的特殊需求。这个用户必须拥有BACKUP_ADMIN权限,这是 MySQL 8.0 新增的权限,用于在集群恢复(recovery)阶段读取其他节点的 binlog。缺失此权限,新节点加入时会卡在RECOVERING状态,日志报错Failed to start applier thread for channel 'group_replication_applier'。创建步骤如下(在第一个节点执行):
-- 创建用户(密码必须符合密码策略,建议用 mysql_native_password 插件) CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'StrongPass123!'; -- 授予必要权限 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; GRANT BACKUP_ADMIN ON *.* TO 'repl'@'%'; -- 刷新权限 FLUSH PRIVILEGES; -- 设置组复制恢复通道的凭据 SET GLOBAL group_replication_recovery_get_public_key = ON; CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='StrongPass123!' FOR CHANNEL 'group_replication_recovery';实操心得:密码中必须包含大小写字母、数字和特殊字符,否则 MySQL 8.0 密码策略会拒绝。我们曾因密码太简单(如 'repl123')导致 recovery 通道认证失败,排查了 3 小时才发现是密码策略拦截。
3.4 启动顺序:为什么必须严格遵循“1-2-3”的物理时序
MGR 集群的启动不是并行的,而是有严格的物理依赖关系。错误的启动顺序是集群无法形成最常见的原因。正确流程如下:
- 启动节点 A(Bootstrap 节点):先确保 A 的 my.cnf 中
group_replication_bootstrap_group=ON,然后启动 mysqld 服务。接着登录 MySQL,执行START GROUP_REPLICATION;。此时 A 进入ONLINE状态,但集群只有它自己。 - 启动节点 B:B 的 my.cnf 中
group_replication_bootstrap_group=OFF,启动 mysqld 后,执行START GROUP_REPLICATION;。B 会向 seeds 列表中的 A 发起连接,A 将其接纳,B 状态变为RECOVERING(同步中),完成后变为ONLINE。 - 启动节点 C:同 B,向 seeds 列表发起连接,最终三节点全部
ONLINE。
若跳过步骤 1,直接启动 B 和 C,它们会互相尝试连接,但因无引导节点,最终都卡在RECOVERING状态。若在 A 启动后未执行START GROUP_REPLICATION就启动 B,B 会因无法连接到有效集群而报错The group communication engine is not active。我们为此编写了start-mgr-cluster.sh脚本,内置 30 秒等待和状态轮询,确保前一节点ONLINE后再启动下一个。
3.5 状态监控:读懂 performance_schema 中的 5 张关键表
MGR 的健康状态不体现在SHOW PROCESSLIST,而深藏于 performance_schema 数据库。日常巡检必须掌握以下 5 张表:
- replication_group_members:集群成员全景图。关注
MEMBER_STATE(ONLINE/RECOVERING/OFFLINE/UNREACHABLE)和MEMBER_ROLE(PRIMARY/SECONDARY)。若某节点MEMBER_STATE=UNREACHABLE,说明网络或防火墙问题。 - replication_group_member_stats:性能指标中枢。重点关注
COUNT_TRANSACTIONS_IN_QUEUE(待处理事务数,持续 > 1000 表示同步延迟)、COUNT_TRANSACTIONS_CHECKED(已验证事务数)、COUNT_TRANSACTIONS_ROWS_VALIDATING(正在验证的行数)。 - replication_connection_status:恢复通道状态。检查
SERVICE_STATE是否为ON,LAST_HEARTBEAT_TIMESTAMP是否在 5 秒内更新。 - replication_applier_status_by_coordinator:协调器状态。
THREAD_ID不为空且SERVICE_STATE=ON表示恢复线程正常。 - replication_applier_status_by_worker:工作线程状态。
LAST_APPLIED_TRANSACTION_ORIGINAL_COMMIT_TIMESTAMP应与当前时间差 < 1 秒,否则存在延迟。
我们用一个简单的 Bash 脚本每分钟抓取这些值,当COUNT_TRANSACTIONS_IN_QUEUE > 5000或MEMBER_STATE != 'ONLINE'时,自动发企业微信告警。这比依赖 Zabbix 的黑盒监控精准得多。
3.6 容灾演练:如何安全地模拟“杀掉一个节点”
纸上谈兵不如真刀真枪。我们每月进行一次容灾演练,步骤如下:
- 在节点 C 上执行
sudo systemctl stop mysql,模拟宕机。 - 立即在节点 A 和 B 上执行
SELECT * FROM performance_schema.replication_group_members;,确认 C 状态变为OFFLINE,A 和 B 仍为ONLINE。 - 持续写入测试数据(如
INSERT INTO test_table VALUES (UUID(), NOW());),观察COUNT_TRANSACTIONS_IN_QUEUE是否归零。 - 重启节点 C:
sudo systemctl start mysql,然后执行START GROUP_REPLICATION;。 - 观察 C 状态是否从
RECOVERING变为ONLINE,且COUNT_TRANSACTIONS_IN_QUEUE先飙升后归零。
注意:演练中绝不能执行
SET GLOBAL group_replication_bootstrap_group=ON在 C 上!这会创建新集群。C 必须以普通成员身份加入现有集群。
3.7 应用适配:你的代码可能正在悄悄破坏 MGR 的一致性
MGR 的强一致性是以牺牲部分灵活性为代价的。应用层必须遵守三条铁律:
- 禁止跨库事务:MGR 要求事务的所有修改必须在同一数据库(schema)内。
BEGIN; INSERT INTO db1.t1 ...; INSERT INTO db2.t2 ...; COMMIT;会直接报错ERROR 3092 (HY000): The table does not exist in the group replication members.。解决方案是拆分为两个独立事务,或使用数据库代理(如 ProxySQL)做路由。 - 禁止非事务性引擎:MyISAM 表不支持事务,MGR 无法对其加锁和验证。所有表必须为 InnoDB。
- 谨慎使用大事务:一个事务修改 100 万行,MGR 需为每一行生成 write set 并广播,极易导致网络拥塞和超时。我们规定单事务 DML 行数上限为 1 万,超限时由应用分批处理。
我们曾有个订单服务,因一个UPDATE orders SET status='shipped' WHERE create_time < '2023-01-01'语句影响 50 万行,导致集群心跳超时,节点被误驱逐。后来改为WHERE id BETWEEN ? AND ?分页执行,问题消失。
4. 实操过程与核心环节实现:从零开始搭建三节点集群的完整流水线
4.1 环境准备:三台 Ubuntu 20.04 虚拟机的标准化初始化
我们使用 VirtualBox + Vagrant 管理测试环境,但生产环境推荐 KVM 或云厂商的 ECS。三台机器配置完全一致:2 核 CPU、4GB 内存、50GB SSD 系统盘、100GB HDD 数据盘(挂载到/data/mysql)。初始化脚本init-ubuntu2004.sh包含以下关键步骤:
# 1. 更新系统并安装基础工具 sudo apt update && sudo apt upgrade -y sudo apt install -y vim curl wget net-tools iproute2 # 2. 创建 MySQL 数据目录并授权(避免默认 /var/lib/mysql 空间不足) sudo mkdir -p /data/mysql sudo chown -R mysql:mysql /data/mysql # 3. 配置时区和 locale(避免时间戳混乱) sudo timedatectl set-timezone Asia/Shanghai sudo locale-gen en_US.UTF-8 # 4. 关闭 swap(MySQL 对 swap 敏感,可能导致 OOM Killer 杀进程) sudo swapoff -a echo '# Disable swap' | sudo tee -a /etc/fstab sudo sed -i '/swap/d' /etc/fstab # 5. 调整内核参数(优化网络和内存) echo 'net.core.somaxconn = 65535' | sudo tee -a /etc/sysctl.conf echo 'vm.swappiness = 1' | sudo tee -a /etc/sysctl.conf sudo sysctl -p实操心得:
vm.swappiness=1是关键。Ubuntu 默认为 60,这意味着内核会积极将内存页换出到 swap,而 MySQL 的 buffer pool 若被 swap,性能会断崖式下跌。设为 1 后,仅在极端内存不足时才使用 swap,极大提升稳定性。
4.2 MySQL 8.0.25 的精准安装与服务配置
Ubuntu 20.04 官方源的 MySQL 8.0.25 是首选,避免手动下载二进制包带来的依赖风险。安装命令如下:
# 添加官方源(确保获取最新安全补丁) wget https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb # 在交互界面中选择 MySQL Server & Cluster -> mysql-8.0 -> Apply sudo apt update sudo apt install -y mysql-server安装完成后,不要运行sudo mysql_secure_installation,因为它会禁用 root 远程登录,而 MGR 节点间通信需要 root 权限。我们改用 SQL 命令精细化加固:
-- 登录 MySQL(默认 root 无密码) sudo mysql -- 创建专用管理用户(替代 root 远程访问) CREATE USER 'mgradmin'@'%' IDENTIFIED BY 'MgrAdminPass!2024'; GRANT ALL PRIVILEGES ON *.* TO 'mgradmin'@'%' WITH GRANT OPTION; -- 删除匿名用户 DELETE FROM mysql.user WHERE User=''; -- 刷新权限 FLUSH PRIVILEGES;接着,编辑/etc/mysql/mysql.conf.d/mysqld.cnf,在[mysqld]段落下添加前文所述的 12 个 MGR 参数。特别注意datadir必须指向/data/mysql,socket改为/data/mysql/mysql.sock,以匹配我们创建的数据目录。
4.3 第一个节点(A)的引导与集群创建
节点 A(192.168.1.10)是集群的“心脏起搏器”。操作必须精确:
# 1. 编辑 my.cnf,设置 group_replication_bootstrap_group=ON sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf # 在 [mysqld] 下添加: # group_replication_bootstrap_group=ON # 2. 重启 MySQL 使配置生效 sudo systemctl restart mysql # 3. 登录并创建复制用户(如前所述) sudo mysql -u root -e " CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'StrongPass123!'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; GRANT BACKUP_ADMIN ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES; SET GLOBAL group_replication_recovery_get_public_key = ON; CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='StrongPass123!' FOR CHANNEL 'group_replication_recovery'; " # 4. 启动组复制(关键一步!) sudo mysql -u root -e "START GROUP_REPLICATION;" # 5. 验证状态 sudo mysql -u root -e "SELECT * FROM performance_schema.replication_group_members;" # 输出应显示:MEMBER_ID(A的UUID), MEMBER_HOST=192.168.1.10, MEMBER_PORT=3306, MEMBER_STATE=ONLINE, MEMBER_ROLE=PRIMARY提示:
START GROUP_REPLICATION执行后,需等待 10-15 秒再查状态。MGR 初始化 Paxos 状态机需要时间,立即查询可能返回空。
4.4 第二、三个节点(B 和 C)的加入与集群固化
节点 B(192.168.1.11)和 C(192.168.1.12)的配置与 A 几乎相同,唯独group_replication_bootstrap_group必须为 OFF。加入流程如下:
# 在节点 B 上执行(C 同理,仅 IP 地址不同) # 1. 确保 my.cnf 中 group_replication_bootstrap_group=OFF sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf # 2. 重启 MySQL sudo systemctl restart mysql # 3. 创建相同的复制用户(密码必须一致!) sudo mysql -u root -e " CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'StrongPass123!'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%'; GRANT BACKUP_ADMIN ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES; SET GLOBAL group_replication_recovery_get_public_key = ON; CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='StrongPass123!' FOR CHANNEL 'group_replication_recovery'; " # 4. 启动组复制(此时 seeds 指向 A 和 C,但 C 尚未启动,A 是唯一种子) sudo mysql -u root -e "START GROUP_REPLICATION;" # 5. 检查状态(B 应先为 RECOVERING,几分钟后变为 ONLINE) sudo mysql -u root -e "SELECT * FROM performance_schema.replication_group_members;"当 B 和 C 都变为ONLINE后,集群即固化。此时可安全地将 A 的group_replication_bootstrap_group设回 OFF:
-- 在节点 A 上执行 sudo mysql -u root -e "SET GLOBAL group_replication_bootstrap_group=OFF;"4.5 验证与压力测试:用真实数据检验集群韧性
集群ONLINE不代表万事大吉。必须进行两项验证:
1. 一致性验证:在 A 上创建测试表并插入数据,检查 B 和 C 是否实时同步。
-- 在节点 A 执行 CREATE DATABASE mgr_test; USE mgr_test; CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50), ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP); INSERT INTO t1 VALUES (1, 'NodeA', NOW());然后在 B 和 C 上执行SELECT * FROM mgr_test.t1;,结果必须完全一致。这是最基本的一致性保障。
2. 故障注入测试:这是最关键的一步。我们使用sysbench进行混合读写压力测试,同时模拟节点宕机:
# 在客户端机器安装 sysbench sudo apt install -y sysbench # 准备 100 万行测试数据 sysbench oltp_read_write --db-driver=mysql --mysql-host=192.168.1.10 --mysql-port=3306 --mysql-user=mgradmin --mysql-password=MgrAdminPass!2024 --tables=1 --table-size=1000000 prepare # 启动压测(16 线程,持续 300 秒) sysbench oltp_read_write --db-driver=mysql --mysql-host=192.168.1.10 --mysql-port=3306 --mysql-user=mgradmin --mysql-password=MgrAdminPass!2024 --threads=16 --time=300 --report-interval=10 run # 在压测进行到 60 秒时,执行:sudo systemctl stop mysql on Node C # 观察压测是否继续(应无缝切换到 A/B),错误率是否突增(理想情况 < 0.1%) # 120 秒后,重启 Node C,观察其是否自动加入并同步实测结果:在 16 线程、300 秒压测中,当 C 节点宕机时,TPS 从 1250 微降至 1220(-2.4%),无事务失败;C 重启后 92 秒完成同步,COUNT_TRANSACTIONS_IN_QUEUE从峰值 8500 降至 0。这证明集群在真实负载下坚如磐石。
5. 常见问题与排查技巧实录:那些让我们熬夜到凌晨的坑
5.1 问题速查表:症状、原因与一键修复命令
| 症状 | 可能原因 | 诊断命令 | 修复方案 |
|---|---|---|---|
SELECT * FROM performance_schema.replication_group_members;返回空 | MySQL 未启动,或 performance_schema 未启用 | sudo systemctl status mysql;sudo mysql -e "SHOW VARIABLES LIKE 'performance_schema';" | 确保performance_schema=ON(my.cnf 中),重启 mysqld |
节点状态为UNREACHABLE | 防火墙阻止组复制端口,或group_replication_local_addressIP 错误 | telnet 192.168.1.10 33061(从 B 测试连通 A);sudo ss -tuln | grep 33061 | 检查 ufw 规则,确认local_address使用本机实际 IP(非 127.0.0.1) |
节点状态为RECOVERING且长时间不变化 | 复制用户权限不足(缺BACKUP_ADMIN),或group_replication_recovery通道凭据错误 | sudo mysql -e "SELECT * FROM performance_schema.replication_connection_status WHERE CHANNEL_NAME='group_replication_recovery';" | 重新执行CHANGE MASTER TO ... FOR CHANNEL 'group_replication_recovery',确认用户有BACKUP_ADMIN |
START GROUP_REPLICATION报错Plugin 'group_replication' is disabled | plugin_load_add未正确配置,或group_replication.so文件不存在 | sudo mysql -e "SHOW PLUGINS;" | grep group;ls /usr/lib/mysql/plugin/group_replication.so | 确认plugin_load_add='group_replication.so'在 my.cnf 中,且路径正确 |
| 集群形成后,新写入数据在其他节点查不到 | binlog_format不是ROW,或enforce_gtid_consistency=OFF | sudo mysql -e "SELECT @@binlog_format, @@enforce_gtid_consistency;" | 修改 my.cnf,重启 mysqld |
COUNT_TRANSACTIONS_IN_QUEUE持续 > 5000 | 网络延迟高,或某节点 I/O 瓶颈(磁盘慢) | ping 192.168.1.10;iostat -x 1 5(在各节点执行) | 升级网络至 10Gbps;将 MySQL datadir 迁移至 SSD |
5.2 “脑裂”(Split-Brain)的识别与紧急手术
脑裂是 MGR 最严重的故障:集群分裂为两个独立子集,各自接受写入,导致数据永久不一致。虽然 MGR 的多数派机制使其极难发生,但在极端网络分区(network partition)下仍有可能。识别方法:在节点 A 上执行SELECT * FROM performance_schema.replication_group_members;,发现只有 A 和 B;在节点 C 上执行,发现只有 C。此时,A 和 B 组成 quorum(2/3),C 被驱逐,但 C 仍认为自己在线。
紧急手术步骤(必须按顺序):
- 立即停止所有写入:通知应用团队,切断所有对集群的写请求。
- 确定“真理”集群:检查
SELECT @@server_uuid;在 A 和 B 上,取server_uuid字典序较小者作为“主集群”(通常为最先启动的节点)。假设 A 的 UUID 更小。 - 重置“孤儿”节点 C:在 C 上执行
STOP GROUP_REPLICATION; RESET MASTER; RESET SLAVE ALL;。这会清空 C 的所有复制元数据。 - 强制 C 以新成员身份加入:在 C 的 my.cnf 中,确保
group_replication_bootstrap_group=OFF,然后START GROUP_REPLICATION;。 - 验证数据一致性:使用
pt-table-checksum工具对比 A 和 C 的关键表,若有差异,需从业务日志人工修复。
注意:绝不可在 C 上执行
SET GLOBAL group_replication_bootstrap_group=ON!这会创建新集群,灾难无法挽回。
5.3 日志分析:读懂 error.log 中的“死亡预告”
MySQL error.log 是故障的晴雨表。以下日志片段预示着即将发生的严重问题:
[Warning] Plugin group_replication reported: 'Member with address xxx:3306 has become unreachable.'
这是心跳超时的警告,若连续出现 3 次,该节点将被驱逐。立即检查网络和group_replication_member_expel_timeout(默认 5 秒,可调至 10 秒以适应高延迟网络)。
