先说结论:MGR(MySQL Group Replication)加上KeepAlived这套组合,本身是能扛住生产环境节点故障的,但绝大多数团队第一次搭完,都会在“故障切换后业务连不上新主库”这个环节翻车。原因不是MGR不行,而是很多人把KeepAlived当成了一个单纯的“绑VIP”工具,没有让它在MGR的选主逻辑里真正找到“谁才是当前主库”。
这篇文章会从架构思路、环境准备、MGR搭建、KeepAlived配置、故障演练到排错速查,完整走一遍生产可用的落地流程。每一步都会解释为什么这么做,也会把我在实际环境里踩过的坑标出来。不管是第一次接触MGR的DBA,还是已经被MGR搞得焦头烂额的运维,按这个流程走下来,至少能少熬几个通宵。
1. 先想清楚:MGR + KeepAlived 这套组合到底在解决什么问题
1.1 MGR 与 KeepAlived 各自负责什么
MGR的核心能力是“组内数据一致”,它通过Paxos风格的共识协议,保证只要多数派节点存活,事务就不会丢。这个机制听起来很完美,但它有一个绕不开的短板——它不提供虚拟IP。也就是说,当主库节点宕机、MGR内部自动选出了新主库后,你的应用不会自动知道该连哪台机器了。
KeepAlived的价值就在于把这个“不知道该连谁”的问题解决掉。它就是为VIP(虚拟IP)漂移而生的,通过VRRP协议让一组节点共享一个IP,谁健康谁就持有这个IP对外提供服务。当主库节点宕机后,KeepAlived检测到异常,会让VIP漂移到新主库所在的那台机器上,业务通过VIP连接,完全无感知。
所以这两个组件并不冲突,而是天然互补:MGR负责数据库内部的数据一致和节点选主,KeepAlived负责对外网络的入口切换。
1.2 节点数量、网络与容灾模型怎么定
在生产环境里,最推荐的是三节点单主模式。原因非常简单,MGR需要多数派才能正常对外服务,三节点可以容忍一个节点故障,两个节点同时挂掉集群就只读了。如果只有两节点,任何一个节点故障都会导致集群无法形成多数派,这和一个节点没区别,还不如传统主从,所以两节点生产环境非常不建议。
三节点怎么放也很有讲究。如果三台都在同一个机柜、同一个交换机底下,整体故障风险其实并不低;如果分散在三个可用区,网络延迟和带宽又会直接影响写入性能。我的经验是:同城三机房间组MGR是性价比最高的方案,延迟通常能控制在1到3毫秒,故障隔离性也能满足大多数业务要求。跨城(比如北京和上海之间)做MGR就得慎重了,写事务的RT会明显增加,不是不能做,但业务方必须能接受这个延迟涨幅。
关于双机房加一个观察节点的方案,我不太建议新手尝试。虽然理论上可以通过调整参数实现,但组复制的多数派机制和网络分区时的行为会让排障变得极其复杂。生产环境求稳,三节点同城是最省心的起点。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一个生产级 MGR 的环境到底需要准备到什么程度
2.1 主机规划与前置检查清单
我先列一下我习惯使用的主机规划模板,三台机器,角色清晰,后面所有命令里的IP都按这个来替换。
| 节点 | IP | 主机名 | 角色 | 系统 |
|---|---|---|---|---|
| db01 | 192.168.10.11 | db01 | MGR主库候选 | CentOS 7.9 / 8.x |
| db02 | 192.168.10.12 | db02 | MGR从库 | CentOS 7.9 / 8.x |
| db03 | 192.168.10.13 | db03 | MGR从库 | CentOS 7.9 / 8.x |
| VIP | 192.168.10.100 | - | 对外服务IP | - |
拿到机器后,不要急着装MySQL,先做以下几项检查,每一项都能排掉后面一个大坑。
时间同步必须提前搞定,MGR对节点间时钟偏差非常敏感,偏差太大会导致事务认证异常甚至节点被误踢。生产环境建议配置NTP或者chrony,并且确认时间同步服务是开机自启的。
防火墙只放行必要端口,MySQL默认的3306端口是业务端口,33061端口是MGR组内通信端口,KeepAlived的VRRP协议报文需要放行。如果用的是系统防火墙,需要确认这两条规则在每台机器上都生效。
hosts文件也很容易出问题。MGR节点之间通过hostname互相识别,如果主机名解析不到对方的IP,启动组复制的时候会直接报错。三个节点的/etc/hosts里都要写上三台机器的主机名和IP对应关系,不要只写其中一两台。
2.2 磁盘与数据目录规划
MySQL对磁盘性能的敏感度这里就不多说了,我这里想强调一个很多团队会忽略的点:redo log和binlog所在的磁盘,尽量不要和系统盘共用。生产环境一旦出现IO抢占,数据库性能会急剧恶化,而MGR本身又有额外的网络和共识开销,对磁盘的稳定延迟要求会比普通主从更高。
数据目录的规划建议单独划分一个数据盘,挂载到独立目录,比如/data/mysql。后面所有配置文件和初始化操作都基于这个路径来写,避免后续扩容或者迁移的时候路径乱掉。
目录创建和权限设置直接在每台机器上执行:
bash复制mkdir -p /data/mysql
chown -R mysql:mysql /data/mysql
如果在安装MySQL前没有创建mysql系统用户,可以先执行:
bash复制useradd -r -s /sbin/nologin mysql
2.3 安装 MySQL 8.0 并完成初始化
生产环境我建议从官网下载二进制tar包安装,版本尽量选择8.0.20以上(本文示例假设为8.0.x版本),因为8.0.20之前MGR有不少bug,尤其是节点加入和退出相关的逻辑,后续版本修复了很多问题。选择二进制包安装的好处是目录结构清晰,便于多实例部署和控制版本。
下载解压并做软链接,以8.0.x为例:
bash复制tar xf mysql-8.0.x-linux-glibc2.12-x86_64.tar.xz -C /usr/local/
ln -s /usr/local/mysql-8.0.x-linux-glibc2.12-x86_64 /usr/local/mysql
echo 'export PATH=/usr/local/mysql/bin:$PATH' >> /etc/profile
source /etc/profile
然后初始化数据目录。MySQL 8.0建议用--initialize-insecure方式先初始化出空密码的root账号,这样后续首次登录不用在日志里翻临时密码,方便整条流程脚本化。
bash复制mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql
启动mysqld并用socket连接:
bash复制cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
service mysqld start
mysql -uroot -S /tmp/mysql.sock
首次登录后立即修改root密码:
sql复制ALTER USER 'root'@'localhost' IDENTIFIED BY
