☰
MySQL高可用架构实战:PXC集群部署与Galera同步复制全解析
2026/9/28 13:40:05 网站建设 项目流程

在互联网公司做DBA这些年,MySQL高可用方案我踩过的坑可以写一本书。最早用的主从复制+VIP漂移,主库一宕机就得手动或者半自动切换,赶上从库binlog追不上,业务损失以分钟计。后来上了MHA,架构复杂度上去了,脑裂问题照样让你凌晨爬起来。再后来我接触了Percona XtraDB Cluster(PXC),一个基于Galera协议的同步多主集群,部署一次之后,我对"高可用"这三个字的理解彻底变了。

这篇文章把我生产环境落地PXC的全过程整理出来,包括为什么选它、环境怎么准备、配置文件里每个wsrep参数到底是什么意思、集群怎么从零拉起、日常该关注哪些指标,最后是我真实踩过的几个坑。适合正在评估MySQL高可用方案的同学,也适合已经决定用PXC但怕踩坑的运维和DBA参考。

1. 为什么是PXC:从"主从"到"多主同步"的选择逻辑

我记得第一次听说PXC是在一个数据库技术分享会上,当时心里第一反应是"多主?那数据冲突怎么解决?"后来系统了解Galera协议才明白,PXC并不是让多个节点随意竞争写入,而是通过一套严格的同步复制机制让每个节点上的事务保持一致。

传统主从复制的核心链路是:主库写binlog,从库拉取binlog然后自己重放。这带来两个绕不开的问题。一个是延迟,主库提交之后从库什么时候追上完全看网络和从库自身压力,赶上大事务或者从库磁盘慢,几分钟甚至几十分钟的延迟都可能出现。另一个是切换风险,主库突然宕机,从库可能还缺最后那部分事务,强制切换的结果就是丢数据。

半同步复制解决了"日志已经收到"的问题,但也只是保证 relay log 写到了从库,至于relay log有没有被真正应用,主库是不关心的。MHA加VIP的方案更成熟,但整体链路长,需要额外维护监控和切换脚本,而且切换时依然有一段不可写的时间窗口。

PXC走的是另一条路。它底层用了Galera协议,节点之间通过gcomm(基于TCP/UDP的组通信)交换写入集(write set)。一个事务在任何节点上提交时,需要先在集群内做certification(认证),让所有节点都确认这个事务不会和别的节点产生冲突,然后要么全集群都提交,要么全集群都回滚。这就是同步复制和"多主一致"的真正含义。

这样说可能有点抽象,我用一个生活化的类比。传统主从像公司只有一个经理做决策,记录完把会议纪要发给副手们,副手们什么时候看完看心情。PXC像所有部门负责人坐在一起开会,任何一个提案必须在场所有人当场点头才算通过,任何一个人否决整场决策就推翻重来。

既然数据一致性这么强,是不是所有场景都该上PXC?不是。它有个必须接受的代价:因为每个事务都要经过集群认证,节点之间的网络延迟会直接叠加到事务响应时间上。跨机房几百毫秒的延迟在PXC里就是不可接受的。另外,如果一个节点突然失联,集群为了保证一致性会把正在提交的事务阻塞或者中止,这种"推进不了"的状态对可用性是有影响的。所以我的建议是:核心交易类、要求数据强一致、并发写入可控的业务适合PXC;跨地域部署、单点写入巨大、对极端可用性要求极高的场景,还是要谨慎评估。

选型时还要注意一点:PXC前身是Codership的Galera Cluster,Percona把它和Percona Server for MySQL封装成了开箱即用的版本,SST同步工具默认用的就是Percona XtraBackup。所以生产环境我更推荐直接用Percona官方源安装,而不是自己拼Galera和MySQL,版本兼容性省心很多。

2. 部署前的环境盘点:节点规划、系统参数与依赖安装

PXC的部署不复杂,但前期的环境准备如果做草率了,后面排错会非常痛苦。我先说节点规划,再说系统参数,最后说软件安装。

2.1 三节点规划:主机名、IP与存储

我这次用的是三台物理机,配置见下面这张表。数据库集群节点不建议少于三个,因为其中一个节点专门用于处理SST传输时,另外两个节点还能继续保持可用。

节点主机名IP地址角色
node1pxc-node1.example.local192.168.10.11初始引导节点
node2pxc-node2.example.local192.168.10.12普通集群节点
node3pxc-node3.example.local192.168.10.13普通集群节点

硬件方面,每个节点配置了8核CPU、32GB内存、两块SSD做RAID1。在PXC里所有节点数据完全一致,所以每个节点的性能配置最好一致,不要出现一个节点是高端服务器其余是低配的情况,写入时慢节点会拖累整个集群。

存储这块我特意强调一下:Galera的认证过程依赖快速IO,SSD是基本要求。机械盘在传统主从上可能只是延迟高一点,PXC里可能直接表现为集群整体变慢甚至超时。另外数据目录独立分区,不要把日志和数据放在同一个逻辑卷。

2.2 系统级准备:hosts、SELinux与防火墙

第一步先做主机名映射,三台机器都要在/etc/hosts里写上三个节点的对应关系:

192.168.10.11 pxc-node1 pxc-node1.example.local 192.168.10.12 pxc-node2 pxc-node2.example.local 192.168.10.13 pxc-node3 pxc-node3.example.local

这样配置的好处是集群地址可以写主机名而不是IP,后续如果换IP不需要改动所有节点的配置。我在生产环境见过有人直接写IP,结果机房调整一次就要全部改一遍,非常被动。

SELinux我直接选择关闭。技术上可以放行相关端口和域,但PXC涉及的进程、端口、SST脚本太多,放行规则写起来繁琐,出问题也不容易排查。测试环境或者对安全要求极高的环境,可以单独写SELinux模块,但我大多数生产环境采用的都是关闭策略。

防火墙需要放行以下几个端口:

端口用途
3306MySQL客户端连接
4444SST全量同步传输(xtrabackup等)
4567Galera集群节点间通信(含组播与单播)
4568IST增量状态传输

命令大概是:

firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --permanent --add-port=4444/tcp firewall-cmd --permanent --add-port=4567/tcp firewall-cmd --permanent --add-port=4567/udp firewall-cmd --permanent --add-port=4568/tcp firewall-cmd --reload

这里有个容易忽略的点:4567不只要放TCP,UDP如果用了组播也要放行,否则节点会发现不了彼此。云主机的话,对应的安全组规则要同步放行。

2.3 安装Percona软件源和PXC软件包

我用的是CentOS 7.9,安装Percona的官方源:

yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm percona-release enable pxc-80 release yum install -y percona-xtradb-cluster

这里建议指定小版本,避免三个节点在滚动更新时出现版本不一致。软件包安装完之后,可以用rpm -ql确认一下libgalera_smm.so的位置,后面配置wsrep_provider要用到:

rpm -ql percona-xtradb-cluster | grep galera

我装的时候这个库默认在/usr/lib64/libgalera_smm.so。不同版本、不同架构路径可能有差异,配置之前先确认一遍,避免启动时报找不到模块。

2.4 系统调优:文件句柄与THP

数据库对系统资源的敏感性远超普通应用。打开文件句柄我先放到65535以上,具体在/etc/security/limits.conf里加上:

mysql soft nofile 65535 mysql hard nofile 65535

然后关闭透明大页THP。MySQL和XtraDB对THP一直不太友好,内存页在数据库场景里抖动会导致性能忽高忽低。关闭方式:

echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag

写进rc.local或者systemd临时脚本里,保证重启后依然生效。这一步不做好,PXC跑起来之后在压测阶段性能曲线会很难看。

3. 配置文件逐项拆解:那些必须懂的wsrep参数

PXC安装完,系统里会带一份/etc/percona-xtradb-cluster.conf.d/目录,里面分成mysqld.cnf、mysqld_safe.cnf(老版本)等文件。我习惯把所有关键配置放在一个地方统一管理,避免改参数时漏掉某个文件。

三个节点的配置文件大体相同,区别只有节点名和地址。下面是核心配置,我加上了注释,关于每个参数为什么这么设,下面逐一展开。

[mysqld] server_id=11 binlog_format=ROW default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 wsrep_provider=/usr/lib64/libgalera_smm.so wsrep_cluster_name=pxc-production-cluster wsrep_cluster_address=gcomm://pxc-node1,pxc-node2,pxc-node3 wsrep_node_name=pxc-node1 wsrep_node_address=pxc-node1 wsrep_sst_method=xtrabackup-v2 wsrep_sst_auth="sstuser:sstpasswd" wsrep_slave_threads=8

3.1 三个"必须":ROW格式、InnoDB与autoinc锁模式

第一个必须:binlog_format必须是ROW。Galera的写集是基于行的,如果binlog格式是STATEMENT或者MIXED,部分基于语句的复制在并发冲突检测时会产生不一致的判定结果。这个参数直接写死,没有商量余地。

第二个必须:default_storage_engine是InnoDB。PXC只支持InnoDB(以及XtraDB),MyISAM表在PXC里创建出来也是不可复制的。如果你把历史遗留的MyISAM表迁移到PXC,记得先用ALTER TABLE改成InnoDB。

第三个必须:innodb_autoinc_lock_mode=2。这是Galera的硬性要求,改回1会导致自增主键在并发写入时产生冲突。原因在于PXC多个节点都可能分配自增值,传统主从模式下主库串行分配自增值没问题,而多主场景必须放弃"表级锁"式的连续分配,改用interleaved交错式分配。对应用来说唯一的区别是自增ID不再严格连续,如果业务层依赖这个,需要提前评估。

3.2 集群通信四个参数:provider、cluster_name、cluster_address、node_name

wsrep_provider指向Galera库文件。它决定了mysqld启动时加载哪个同步引擎,路径不对会直接启动失败。

wsrep_cluster_name是集群的逻辑名称,三个节点必须完全一致。这个名字同时用于区分多个PXC集群,它不参与网络路由,只是校验节点是否属于同一个集群。命名规范建议带上环境后缀,比如pxc-prod-cluster-01,方便区分。

wsrep_cluster_address是集群成员列表,推荐写法是gcomm://节点1,节点2,节点3。这里可以写主机名也可以写IP,配合hosts文件用主机名最省心。注意:这个列表不是"本机连哪几个",而是"整个集群的成员有哪些",每个节点都要写全。

wsrep_node_name和wsrep_node_address是本节点的身份标识。node_name建议唯一,方便在监控里区分节点。

3.3 SST方式与认证:为什么我选xtrabackup-v2

SST(State Snapshot Transfer,状态快照传输)是新节点加入集群时获取全量数据的方式。PXC支持mysqldump、xtrabackup等。我在生产环境只推荐xtrabackup-v2。理由很直接:mysqldump在几GB的数据上还能忍,到了几百GB以上几乎不可用,而且逻辑备份在恢复大表时会产生大量随机IO。xtrabackup是物理备份,基于InnoDB的redo/undo机制做热备份,速度快得多,适合大数据量。

wsrep_sst_auth是SST同步时使用的数据库账号和密码。这个账号要预先在引导节点上创建好,不然第二个节点加入时SST认证会失败。后面启动部分我会给出具体的创建命令。

3.4 复制线程数与常规性能参数补全

wsrep_slave_threads是节点上用于并行应用复制写集的线程数。我一般设置为CPU核数,也可以按核数的1-2倍配,实测8核机器设8个线程比较稳妥。这里不要拍脑袋设一个很大的数,线程过多反而增加上下文切换开销。

常规的MySQL性能参数,比如innodb_buffer_pool_size、max_connections、innodb_log_file_size,依然按单实例的标准做配置。PXC只是复制层不同,存储引擎层还是InnoDB那一套。我这次32GB内存,buffer pool分配到20GB,具体的值需要结合你机器的实际负载调整。

另外别忘了在mysqld_safe或者systemd环境里加一条limit设置,文件句柄和进程数都放开,配合前面limits.conf里的ulimit配置,双保险。

4. 集群拉起全过程:从零节点引导到三节点全部加入

配置准备好之后,进入最关键的启动环节。这里有一个必须记住的原则:三节点集群第一次启动,只能有一个节点使用引导模式(bootstrap),其他节点用普通模式启动。如果多个节点同时用引导模式,等于同时产生两个"主"分区,后果就是脑裂。

4.1 引导第一个节点:bootstrap只在本次有效

第一个节点执行:

systemctl start mysql@bootstrap

这条命令在CentOS 7/8的systemd环境下等价于mysqld_safe --wsrep-new-cluster。它告诉Galera"我是第一个节点,我拉起一个新的集群分区"。启动完成后,登录MySQL查看状态:

SHOW STATUS LIKE 'wsrep_cluster_size'; SHOW STATUS LIKE 'wsrep_local_state_comment';

正常情况下wsrep_cluster_size=1,wsrep_local_state_comment=Synced。这个节点现在是一个只有自己的单节点集群,但是数据写入已经进入Galera模式了。

4.2 创建SST同步账号

在引导节点上执行:

CREATE USER 'sstuser'@'localhost' IDENTIFIED BY 'sstpasswd'; GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'sstuser'@'localhost'; FLUSH PRIVILEGES;

如果安装的是PXC 8.0,还需要加上BACKUP_ADMIN权限,8.0的xtrabackup需要这个权限才能做备份和恢复:

GRANT BACKUP_ADMIN ON *.* TO 'sstuser'@'localhost';

这个账号为什么用localhost?因为SST传输时,xtrabackup是在本机通过socket连到本机的mysqld来备份的,从SST发起方视角看就是本机连接,不需要远程访问。这里踩坑的人很多,我见过有人把host设成'%',反而增加了暴露面。

4.3 启动第二、第三个节点:正常systemctl start即可

第二、第三个节点执行:

systemctl start mysql

对,就是普通的启动。节点启动后,会发现wsrep_cluster_address里有成员列表,于是通过gcomm协议与引导节点同步元数据,然后触发SST流程:从已有节点拉取一次全量数据,应用完成之后进入Synced状态。

这里有个很容易误操作的点:如果集群已经存在,某节点重启一定不要用mysql@bootstrap。我见过新人运维在节点故障后手动启动了bootstrap,直接把集群从Primary分区里分离出去,造成全集群不可写。正确操作是普通启动即可,节点会自动找现有集群并同步数据。

4.4 验证集群状态

三个节点都启动后,在任意节点执行:

SHOW STATUS LIKE 'wsrep_%';

重点看几个值:

状态项期望值含义
wsrep_cluster_size3集群成员数
wsrep_local_state_commentSynced本节点已同步
wsrep_cluster_statusPrimary集群处于主分区
wsrep_connectedON本节点已连接集群

再做一个写测试:在node1上建一张测试表并插入数据,回到node2和node3上查,数据应该立即可见。这一步验证的是同步复制是否真的生效,也可以顺手验证应用连接串随便指向哪个节点都能读到最新数据。

4.5 重置引导标记与后续启动顺序

集群正常运行后,每次重启集群不一定要用node1引导。选择哪个节点做bootstrap,关键是确认它拥有最新的完整数据。更稳妥的做法是:把集群停掉后,检查每个节点数据目录下的grastate.dat,找到safe_to_bootstrap=1的节点,用它执行bootstrap,再让其他节点普通启动。

这个文件的路径在数据目录下,比如/var/lib/mysql/grastate.dat。文件内容包含seqno和safe_to_bootstrap字段。每次干净关闭节点后seqno会递增,safe_to_bootstrap标记只有最后一个正常关闭且数据完整的节点才会置1。如果所有节点非正常宕机,safe_to_bootstrap可能都是0,这时候就要靠人工判断数据完整性来选引导节点了。

5. 日常运维的几个关键动作:备份、参数调整与状态监控

PXC上线的第一周,我几乎每天都要盯着状态指标。很多问题在指标上是先有预兆的,等真正报错已经晚了。这一节我分享三个日常动作:怎么备份、怎么调整参数、怎么看指标。

5.1 备份策略:物理备份优先,注意单节点IO

因为PXC所有节点数据一致,理论上备份任何一个节点都能拿到完整数据。我建议用Percona XtraBackup(PXB)对其中一个节点做物理备份:

xtrabackup --backup --target-dir=/backup/pxc-full-$(date +%F) \ --user=backup_user --password=xxx --host=127.0.0.1

备份时再加上binlog的增量备份,形成"全量+增量"的组合,恢复时先还原全量,再应用增量binlog。这里有个PXC特有的注意点:备份节点本身也是承担流量的,PXB全量备份期间会读大量数据,对其他节点的写入会造成流控(flow control)。所以大库备份一定安排在业务低峰,或者轮换着从不同节点备份,避免某节点长期高IO。

另外PXC 8.0的PXB工具要选择与实例大版本匹配的版本,比如8.0.30的PXC要用8.0.34版本的PXB来备份恢复,版本错位会导致无法识别数据文件。

5.2 参数调整:静态参数与动态参数分开对待

PXC大部分wsrep参数是静态的,修改后必须重启节点。重启单个节点不影响集群可用性,但需要注意滚动顺序:逐个重启,确保每次集群中至少有N-1个节点存活。因为PXC需要多数派节点才能正常提交事务,三节点集群如果有两个节点同时不可用,剩下的那一个节点会进入non-Primary状态,整个集群不可写。

动态参数可以用SET GLOBAL的方式修改,但要注意:动态修改只对当前节点生效。想把配置固化的话,记得把参数同步写进配置文件,否则下次重启就还原了。

有一个参数我特别提一下:wsrep_max_ws_size和wsrep_max_ws_rows。这两个限制了单个事务的写集大小。默认配置对绝大多数业务够用,但如果在PXC里执行超大事务(比如批量更新几百万行),可能直接报"transaction too large"的错误。遇到这种场景,可以适度调大限制,但根本上还是要从应用侧拆分大事务。

5.3 状态指标:我看哪几个值判断集群健康

日常巡检我用一条SQL就够了:

SHOW GLOBAL STATUS LIKE 'wsrep_%';

重点关注:

  • wsrep_cluster_status:必须是Primary,一旦显示non-Primary,意味着节点失去了多数派,集群进入保护状态。
  • wsrep_flow_control_paused:反映节点被流控阻塞的时间比例。持续大于0.5说明集群某个节点写入慢,拖慢了整体。
  • wsrep_last_committed:三个节点这个值应当基本接近,差距过大说明某个节点同步滞后。
  • wsrep_local_state_comment:Synced是正常,Joining/Donor出现时说明正在做SST或IST。

这三个节点之间的延迟监控,我直接写了一个简单的shell脚本,每30秒轮询一次,把wsrep_last_committed的差值打印出来,超过阈值就告警。比看负载和CPU更能直接反映Galera层的健康状况。

6. 我踩过的坑:启动顺序、磁盘空间、网络超时与慢速SST

这一节是全文最"贵"的部分。以下问题我在测试环境和生产环境都真实遇到过,有些坑在官方文档里写得不够直白,我在这里完整还原排查链路。

6.1 第二节点加入时SST一直失败的排查

现象:node2执行systemctl start mysql后,日志里反复出现"State transfer failed"或者"WSREP: Failed to prepare for state transfer"。

排查思路:先看错误日志。PXC的错误日志路径在/var/log/mysql/error.log,搜索"WSREP"相关条目。通常能看到两种原因:一种是SST认证失败,提示access denied;另一种是SST传输过程中断。

如果是认证失败,检查wsrep_sst_auth里写的账号密码是否和4.2节创建的一致,注意别把创建时用的host写错。

如果是传输中断,十有八九是磁盘空间。xtrabackup做SST时,在Donor节点(提供数据的节点)和Joiner节点(新加入节点)两端都需要临时存储空间。Joiner节点数据目录所在分区的可用空间小于数据总量时,xtrabackup stream到一半就会报错。我当时遇到的坑就是node2的数据盘只给了50GB,而主节点数据已经有80GB,SST跑一会就把磁盘写满了。

解决方式:数据盘扩容到数据的1.5倍以上,或者临时往另一个磁盘写SST,再调整xtrabackup相关临时目录配置。这里给一个明确判断命令:

df -h /var/lib/mysql

如果使用率超过90%,先解决空间问题再谈SST。

6.2 节点重启后反复走慢速SST

现象:node3只是宕机了10分钟,启动后不是走IST增量同步,而是又做了一次全量SST,几小时才恢复。

原因分析:SST和IST的选择逻辑取决于Joiner在加入时,集群里是否还保留着它缺失的那部分事务。如果Joiner的grastate.dat里seqno比Donor落后太多,超出了Donor的保留窗口,就必须走全量SST。另外,如果Joiner数据的safe_to_bootstrap被标记为0且无法验证数据完整性,也会退回到全量SST。

这里我先解释一下IST和SST的区别。SST是"把整份数据打包给你",对应的是全量快照传输,速度慢且消耗大量网络和磁盘。IST是"你缺多少,我给你补多少",基于Donor的gcache缓冲做增量传输,快得多。日常节点短时间重启都应该走IST,如果每次都全量SST,多半是配置或者数据清理策略有问题。

我当时排查发现,是我在配置文件里把wsrep_sst_method改成了mysqldump(为了测试对比),mysqldump无法做增量SST,于是一律全量。换回xtrabackup-v2并确保gcache够大之后,节点重启基本都走IST。

这里补充一个重要的运维习惯:如果节点只是进程崩溃而数据文件完整,启动前可以先检查grastate.dat里的seqno比Donor落后多少,如果差距很小,直接普通启动,多数会走IST。不要贸然删除grastate.dat,删除后节点必然全量SST。

6.3 两个节点同时失联,剩下的节点变non-Primary

现象:机房网络抖动,node2和node3同时断连,node1日志里出现"WSREP: failed to reach primary view"并且数据库只读不可写。

原因:PXC是多数派原则。三节点集群只有node1一个存活,存活节点数不满足多数派,为了防脑裂集群主动进入non-Primary状态,拒绝写入。这是设计行为,不是故障。此时如果node2、node3恢复网络,三个节点重新握手后会恢复Primary。

我在这里吃过一个亏:当时误以为node1坏了,手动在node1上执行了bootstrap试图拉起新集群,结果node2、node3恢复之后发现已经分离成两个分区,数据出现分裂。正确的做法是等待失联节点网络恢复,让集群自己恢复多数派视图。

如果失联节点确实无法恢复(比如物理宕机),需要人工介入:保留数据完整的节点作为新引导节点,其他节点重新加入。这种场景下,操作顺序和之前章节说的引导逻辑一致,关键是要确认哪个节点的seqno最新、数据最完整。

6.4 慢速SST和网络带宽的纠缠

SST同步本质上是一次全量数据拷贝,几百GB数据在千兆网卡上跑,传输阶段就要几十分钟,加上xtrabackup的备份和恢复,整体可能数小时。我遇到过因为SST占用带宽导致业务查询变慢的情况。

解决思路有三个层面:一是业务低峰期进行节点扩容,二是专门配一条内网高速链路用于节点间数据传输(比如万兆网卡),三是给SST配置限速,通过wsrep_sst_xtrabackup相关的参数控制备份速率,避免拖垮业务流量。

另外一个容易被忽视的细节:云主机如果用了公网IP互连做集群,SST流量会走公网,不仅慢还可能有额外费用。生产环境一定要用内网互通地址配置wsrep_cluster_address。

最后说一句个人体会。PXC让DBA在"数据一致性"这件事上省了很多心,但对运维的基本功要求反而更高了——你得清楚理解每个节点启动时到底在做什么,得习惯在出问题的时候先看grastate.dat和error log而不是凭感觉重启。把部署和启动逻辑吃透了,PXC是一个相当省心的集群;反之,任何一个细节的疏忽,都会在后续某次故障里加倍还回来。

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

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

立即咨询