我最早被 PXC + ProxySQL 这套组合打动,是因为一次凌晨两点的数据库“脑裂”事故。当时业务跑在传统主从复制上,由于网络抖动触发了自动切换脚本,结果主库和从库两边都以为自己是主库,双写加上不完整的回放,半小时内把核心业务表的数据搞出了好几处不一致。从那次以后,我对所有依赖VIP漂移、切换脚本来维持高可用的方案都心存怀疑。后来在朋友的推荐下,我把核心业务库迁移到了 PXC(Percona XtraDB Cluster)加 ProxySQL 的架构,彻底把“故障后抢救”变成了“故障时自动绕过”。这篇文章就围绕这套组合,把从选型、部署、配置到故障演练的完整过程掰开揉碎讲一遍,尤其适合那些正在被主从延迟、切换脑裂、读扩展难困扰的 DBA 和架构师。
1. 为什么是 PXC + ProxySQL,而不是“主从复制 + MHA”
1.1 传统主从复制在高可用上的三个致命伤
传统一主一从加 MHA 或者 Keepalived 的方案,核心思路是“出了故障再切换”。但这个思路本身有三个绕不开的先天问题。
第一个问题是切换逻辑只能尽量逼近正确,却不能保证绝对正确。MHA 这类工具在做 failover 时需要先判断主库的 binlog 有没有补全,再从多个从库中挑选数据最新的节点,还要处理病节点上残留的事务。任何一步判断错误,就会出现我开头说的那种脑裂或数据丢失。尤其当网络抖动导致主库和从库互相无法通信时,切换脚本根本分不清哪边是正常人。
第二个问题是延迟。异步复制下,从库的数据和主库之间天然有时间差。读写分离后读流量打到从库,读到的数据可能是几十毫秒甚至几秒之前的快照。对订单、库存这类业务来说,这种“刚写入就读不到”的问题很难跟业务解释。
第三个问题是运维成本高。主从切换意味着要处理新的主从关系、调整 relay log、重新指定 VIP,整个流程至少得维护一套复杂的切换脚本,还要反复做演练,否则真出故障时脚本往往跑不通。
1.2 Galera 同步复制带来的质变
PXC 底层用的 Galera 库复制机制和传统主从是两回事。它采用的是同步复制和证书认证机制:一个事务在一个节点提交时,会把写集广播到集群里的所有节点,其他节点对写集做冲突认证,认证通过后提交才算成功,否则事务会被回滚。换句话讲,客户端感知到的是整个集群一起提交成功,而不是主库提交了再从库慢慢追。
这套机制带来的直接好处是:任何节点上写入的数据,在事务提交成功的那一瞬间,集群内所有正常节点都拥有这些数据,不存在主从延迟问题。同时,PXC 的节点是真正意义上的多主,写流量可以打到任意节点,节点之间天然共享同一份数据状态。
当然,PXC 不是没有代价。同步复制意味着每次提交都要等所有节点确认,写入延迟和网络质量强相关;而且为了确保复制不出错,对表结构、事务隔离级别、锁机制都有严格限制。这些限制我会在第 2 章专门展开。
1.3 ProxySQL 在整套架构里的定位
如果应用直接连 PXC 的三个节点,客户端需要自己维护一份节点地址列表,还得自己实现写节点的故障切换,这在工程上是不可接受的。ProxySQL 是我见过最合适的互补组件,它是一个 MySQL 协议层代理,主要有几点价值:
- 连接池:前端应用连接 ProxySQL,ProxySQL 再维护到后端 PXC 节点的连接池,避免应用频繁创建短连接导致 MySQL 连接数被打满。
- 读写分离:通过 SQL 正则规则或摘要匹配,把读请求转发给读主机组,把写请求和事务内的语句转发给写主机组。
- 健康检查与故障自动绕过:ProxySQL 会定期检查后端节点状态,一旦某个 PXC 节点异常,自动将其从路由表中摘除,新请求不再分发过去。
- SQL 审计和限流:可以在网关层直接对慢 SQL、特定用户或来源 IP 做拦截、重定向和限流,排查问题时特别有用。
1.4 最终架构长什么样
我最终落地的拓扑非常简洁:应用层只面向 ProxySQL 的 6033 端口提供一个数据库账号,ProxySQL 作为唯一数据库入口。后端是三个 PXC 节点,三个节点均部署在同一个机房的同一网段内,通过千兆以上带宽互联。
这套架构中有一个细节需要提前说明:虽然 PXC 支持多主写入,但在实际生产环境,我更倾向于让写流量集中到其中一个节点,读流量由 ProxySQL 负载均衡到全部节点。这样既避免多节点并发写同一行产生的认证冲突,又保留了故障时写流量自动漂移到其他节点的能力。这个“单点写、多点读、故障自动切换”的姿态,是 PXC + ProxySQL 组合最舒服的运行方式。
2. 搭这套环境之前,先想明白的几件事
2.1 网络要求与节点规划
PXC 是同步复制,每个写事务的提交延迟由网络往返时间决定,跨机房部署几乎不可行。同一机房的专线环境下,节点间 ping 最好稳定在 1ms 以内,如果超过 5ms,写入性能下降就会非常明显。建议给三个节点准备独立的千兆或万兆内网网卡,并确保 Oracle 和数据库监控网段分开,避免业务流量冲击集群同步流量。
节点数量方面,PXC 需要奇数个节点。三节点集群能容忍一台节点宕机,五节点集群能容忍两台。更多人会误以为五节点可以容忍三台宕机,这是错的,Galera 的 quorum 机制要求可用节点必须超过总节点数的一半。所以五节点集群最多挂两台,一旦挂了三台,整个集群就进入只读降级状态。
2.2 Galera 对表结构和工作负载的限制
Galera 虽然把“多主复制”变成了现实,但它的复制前提是所有节点上的数据状态一模一样。为了做到这一点,它对数据库使用方式提出了很多严格约束,我用一张表把最重要的几条列清楚:
| 限制项 | 约束说明 | 违反后的现象 |
|---|---|---|
| 存储引擎 | 只允许 InnoDB,MyISAM 表无法参与复制 | DDL 直接被拒绝 |
| 自增锁模式 | innodb_autoinc_lock_mode 必须是 2 | 多主并发插入时自增值冲突 |
| binlog 格式 | 必须使用 ROW | 复制丢行或主键冲突 |
| 临时表 | 禁止会话级临时表 | 严格模式下直接报错 |
| XA 事务 | 不支持分布式事务 | 事务提交失败 |
| 表主键 | 每张表必须有显式主键 | 删除或更新操作无法安全复制 |
如果你以前的业务里用了 MyISAM 表,或者大量使用会话临时表,迁移前就需要先做改造。Percona 在 PXC 里提供了 pxc_strict_mode 参数,生产环境我建议保持默认的 ENFORCING,它会在你违反上述约束时直接报错拦截,而不是等数据出问题了再排查。
2.3 一致性参数怎么定
PXC 的同步复制做到的是“已提交事务在所有节点上可见”,但这里的“可见”有一个细微差别:事务在节点 A 提交成功后,节点 B 可能在极短的一瞬间还没完成 apply。这个时间窗口只有毫秒级,但对某些业务来说,如果读请求刚好被路由到了节点 B,就可能读到旧数据。如果你不希望出现这种情况,需要开启因果一致性机制。
PXC 中可以通过 wsrep_sync_wait 参数控制读操作的一致性等级。我在测试环境中把该参数设置为 5,同时在 ProxySQL 的读路由下做了大量注入验证,确认读己之写没有问题。具体值怎么设,需要结合你的 PXC 版本和业务容忍度,但强烈建议开启,别贪图那一点点性能让业务读到过期数据。也可以使用 wsrep_causal_reads=ON 达到类似效果,两个参数根据版本二选一即可。
2.4 端口、防火墙和主机名
PXC 节点之间通信不只是 MySQL 的 3306 端口,还有三个额外端口。很多新手在部署时只放行了 3306,然后某个节点加入集群或执行 SST 时疯狂报错,排查半天才发现是防火墙问题。这几个端口必须全部对外开放:
| 端口 | 用途 |
|---|---|
| 3306 | MySQL 客户端连接、ProxySQL 健康检查 |
| 4567 | Galera 集群通信端口,节点加入、写集广播 |
| 4568 | 增量状态传输 IST 专用端口 |
| 4444 | 全量状态传输 SST 专用端口 |
另外,三个节点的 /etc/hosts 里必须把另外两个节点的主机名都解析好。我遇到过集群节点之间一切正常,但执行 SST 时提示无法解析主机名的情况,最后就是在 /etc/hosts 里补了一条记录解决。
3. PXC 三节点部署实录:从 bootstrap 到全节点 Synced
3.1 安装与初始账号
我这里的操作系统是 CentOS 7.9,数据库版本是 PXC 8.0。安装阶段建议使用官方仓库来管理后续的补丁更新,先把仓库配置好,再安装软件包:
wget https://repo.percona.com/percona/yum/percona-release-latest.noarch.rpm rpm -ivh percona-release-latest.noarch.rpm percona-release enable-only pxc-80 release dnf install -y percona-xtradb-cluster-80三个节点分别执行同样的安装步骤。安装完成后,MySQL 数据目录会自动初始化,根账号的临时密码会写在日志里。我用下面的命令找到初始密码,并立刻做了修改:
grep 'temporary password' /var/log/mysql/error.log修改完 root 密码后,需要创建一个 SST 专用账号。SST(State Snapshot Transfer)是在新节点加入集群或节点数据差距过大时,通过 xtrabackup 做全量数据同步的手段,这个账号会从数据源节点读取数据。我创建账号时是这样授权的:
CREATE USER 'sst_user'@'%' IDENTIFIED BY 'Sst@123456'; GRANT SELECT, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT, BACKUP_ADMIN ON *.* TO 'sst_user'@'%'; FLUSH PRIVILEGES;这里需要注意,BACKUP_ADMIN 权限是 MySQL 8.0 和 Percona 8.0 做备份时必需的,少了它 xtrabackup 会在备份阶段直接报 permission denied。
3.2 核心参数逐条说明
PXC 8.0 的配置分散在 /etc/percona-xtradb-cluster.conf.d/ 目录下,其中 mysqld.cnf 是主配置文件。三台节点除了 wsrep_node_name 和 wsrep_node_address 不同,其他参数保持一致。我贴一份实际使用的配置模板:
[mysqld] # 基础路径 datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock log-error=/var/log/mysql/error.log pid-file=/var/run/mysqld/mysqld.pid # Galera 相关 wsrep_provider=/usr/lib64/libgalera_smm.so wsrep_cluster_name=pxc-cluster wsrep_cluster_address=gcomm://node1,node2,node3 wsrep_node_name=node1 wsrep_node_address=192.168.1.11 wsrep_provider_options="gcache.size=1G; gcache.page_size=128M; gcache.mem_size=256M" wsrep_sst_method=xtrabackup wsrep_sst_auth=sst_user:Sst@123456 pxc_strict_mode=ENFORCING # 复制相关 binlog_format=ROW server_id=101 log_bin=/var/log/mysql/binlog log_slave_updates=ON sync_binlog=1 # InnoDB 相关 default_storage_engine=InnoDB innodb_autoinc_lock_mode=2 innodb_buffer_pool_size=8G innodb_flush_log_at_trx_commit=1 # 其他 wsrep_slave_threads=4 wsrep_sync_wait=5逐条解释几个关键点。
wsrep_provider 指定 libgalera_smm.so,这是 Galera 的核心库,路径在不同系统上略有差异,CentOS 7 通常是 /usr/lib64/libgalera_smm.so,Ubuntu 通常在 /usr/lib/libgalera_smm.so。
wsrep_cluster_address 是整套配置里最容易出错的参数,它应指向所有节点的地址。但在集群初始化的第一次启动时,第一个节点必须特殊处理,不能直接用这个配置启动,否则它会尝试连接其他尚未启动的节点然后失败。这个细节我在 3.3 节单独讲。
gcache.size 我设置成 1G,作用是让节点在掉线期间尽量把其他节点的写入集缓存在内存中。掉线重连时,如果缺失的数据量没有超过 gcache 容量,节点可以通过增量状态传输 IST 快速追上;如果超过了,就只能做全量 SST 同步。生产环境写量较大的情况下,1G 是起步,我见过一些写密集业务把 gcache.size 调到 4G。
server_id 在三个节点上必须不同,虽然 PXC 内部不依赖 server_id 做复制,但 binlog 的复制链路仍然会用到它。我在三个节点分别配置为 101、102、103。
3.3 正确的启动顺序:第一个节点怎么 bootstrap
PXC 的启动顺序决定了整个集群能不能组起来。第一个节点必须用 bootstrap 模式启动,意思是“我自己构成一个新集群”,后续节点再以普通模式加入。
实践中最稳妥的做法是,在第一个节点上执行 Percona 提供的引导脚本:
galera_new_cluster执行这条命令前,必须确认节点的 data 目录是干净且最新的。如果是全新节点,直接执行没问题;如果是恢复场景,需要先确认 safe_to_bootstrap 标志。
第一条命令执行完,观察日志确认节点已经以 Primary 状态启动,然后立刻回到另外两个节点,分别执行:
systemctl start mysql这里有一个非常容易犯的错误:后续节点启动时用了 galera_new_cluster,导致它没有加入已有集群,而是自己又组了一个新集群。这样两个集群同时存在,数据还会产生分裂,排查起来相当痛苦。记住,只有第一个引导节点能用 galera_new_cluster,后续节点一律用 systemctl start mysql。
3.4 怎么用 SHOW STATUS 确认集群状态
节点启动完成后,通过 MySQL 客户端进入任意节点,执行下面的状态查询:
SHOW STATUS LIKE 'wsrep_cluster_size'; SHOW STATUS LIKE 'wsrep_cluster_status'; SHOW STATUS LIKE 'wsrep_local_state'; SHOW STATUS LIKE 'wsrep_local_state_comment'; SHOW STATUS LIKE 'wsrep_connected';一个健康的三节点集群,预期结果如下:
| 状态项 | 预期值 | 说明 |
|---|---|---|
| wsrep_cluster_size | 3 | 集群共有 3 个节点 |
| wsrep_cluster_status | Primary | 集群处于主分区 |
| wsrep_local_state | 4 | 本地节点状态正常 |
| wsrep_local_state_comment | Synced | 数据已同步 |
| wsrep_connected | ON | 已连接集群 |
只要这些值符合预期,PXC 底层的集群就已经完全健康了。如果 wsrep_local_state_comment 长时间停留在 Joining 或 Donor,说明节点还没有完成状态传输,需要去 error.log 里看 SST 或 IST 的进展。
4. 接入 ProxySQL:读组、写组和防坑原则
4.1 ProxySQL 安装与 admin 配置
在加入 ProxySQL 之前,PXC 集群本身已经能提供多主读写能力了,但应用直接连 PXC 节点会暴露太多细节。ProxySQL 作为一个代理层接入后,应用只需要面对一个逻辑地址即可。
ProxySQL 安装同样走官方仓库,装完版本是 2.x,启动服务并开放管理端口:
systemctl enable --now proxysqlProxySQL 默认管理接口监听在 6032 端口,用户名和密码都是 admin。生产环境第一件事就是改掉这个默认账号密码。可以用管理客户端连接:
mysql -u admin -padmin -h 127.0.0.1 -P 6032连接成功后,升级 admin 用户的密码,并限定管理接口只监听内网地址。由于 ProxySQL 配置分内存、运行时和磁盘三层,修改后需要显式执行持久化和加载命令,这一点我后续会强调。
4.2 让 ProxySQL 原生感知 PXC 集群状态
传统 ProxySQL 配置读写分离时,只知道自己下面的 MySQL 服务器是否存活,但对 Galera 集群的“Primary / Synced”状态一无所知。如果节点失效后还留在读组里,应用就会经常读到错误状态的数据。ProxySQL 2.x 针对 Galera 和 PXC 提供了专门的集群感知能力,需要在 mysql_replication_hostgroups 表里指定 writer 主机组、reader 主机组和检测类型。
先把三个 PXC 节点注册到 mysql_servers 表:
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight, max_connections) VALUES (10, '192.168.1.11', 3306, 2, 500), (10, '192.168.1.12', 3306, 1, 500), (10, '192.168.1.13', 3306, 1, 500);再配置复制主机组映射:
INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, check_type, comment) VALUES (10, 20, 'pxc', 'pxc-cluster');check_type 为 pxc 时,ProxySQL 会周期性执行 SHOW STATUS LIKE 'wsrep_cluster_status' 和 'wsrep_local_state',判断节点是否处于 Primary 且状态为 Synced。只要有一个条件不满足,这个节点就会被自动标记为 OFFLINE,不会再把流量分发过去。这正好弥补了传统方案的盲区。
同时需要为健康检查单独建一个监控账号,权限至少要有 PROCESS 和 SELECT:
CREATE USER 'monitor'@'%' IDENTIFIED BY 'Monitor@123'; GRANT PROCESS, SELECT ON *.* TO 'monitor'@'%';然后在 ProxySQL 管理端配置监控账号并加载:
SET mysql-monitor_username='monitor'; SET mysql-monitor_password='Monitor@123'; LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;4.3 前端用户和路由规则怎么配
应用连 ProxySQL 时使用的是代理端的账号,这个账号需要与后端 PXC 节点上创建的应用账号保持一致。我一般先在 PXC 节点上创建好账号:
CREATE USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'App@2024'; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'%';这里特意用了 mysql_native_password。PXC 8.0 默认的认证插件是 caching_sha2_password,部分版本的 ProxySQL 和旧版驱动在连接时会出现认证失败,尤其是在走公网或特定客户端库时。为了避免这个坑,我干脆在创建应用账号时显式指定 mysql_native_password,代价小,兼容性最好。
然后把这个账号接入 ProxySQL:
INSERT INTO mysql_users (username, password, default_hostgroup, max_connections) VALUES ('app_user', 'App@2024', 10, 500);default_hostgroup 设置为 10,意味着所有没有命中显式路由规则的 SQL 都会默认落到写组。
接下来配置路由规则。我的核心诉求是:普通 SELECT 走读组,SELECT ... FOR UPDATE 回写组,其他 DML 语句一律走写组。对应规则如下:
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (1, 1, '^SELECT .* FOR UPDATE', 10, 1), (2, 1, '^SELECT ', 20, 1);这里有两个容易被忽略的地方。第一,FOR UPDATE 这条规则必须放在普通 SELECT 前面,否则会被第二条拦截到读组,造成锁逻辑失效。第二,INSERT、UPDATE、DELETE 不用专门写规则,因为它们没有匹配到上面的规则,会走用户的 default_hostgroup=10。
4.4 改完配置为什么必须 LOAD TO RUNTIME
ProxySQL 的配置分三层:disk 层持久化、runtime 层生效、memory 层是当前内存里的编辑区。我们在 admin 端 INSERT 的数据只写到了 memory 层,如果不做任何操作,下次重启就丢了;如果不把 memory 推送到 runtime,则不会立即生效。所以每次改完配置都要执行:
LOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK; LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK; LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;一个常见场景是:有人改了 mysql_servers 后没有执行 LOAD,然后在管理端 query 时发现后端状态没变化,误以为健康检测有问题。其实只是配置没有推到 runtime。
配置加载完成后,用应用账号直接连 ProxySQL 的 6033 端口做一个冒烟测试,分别执行 SELECT 和 SELECT ... FOR UPDATE,然后查询 stats_mysql_query_digest 确认 SQL 最终落到了哪个主机组:
SELECT hostgroup, username, digest_text, count_star FROM stats_mysql_query_digest ORDER BY last_seen DESC;在实验结果里,普通 SELECT 的 hostgroup 应该是 20,FOR UPDATE 的 hostgroup 应该是 10。
5. 故障演练实录:节点宕机、整体断电到恢复
5.1 停掉一台节点后发生了什么
集群搭建完并配置好 ProxySQL 后,我做的第一件事不是立刻接入业务,而是先做完整的故障演练。第一步比较温和:直接停掉其中一个 PXC 节点。
我选择停掉 IP 为 192.168.1.12 的节点,执行 mysqladmin shutdown。PXC 集群基于 quorum 机制,三节点中挂掉一个,剩余两个节点仍然构成多数派,集群继续工作,不会进入只读模式。
观察 ProxySQL 管理端的状态,几秒后 mysql_servers 中那个节点就会被标记为 OFFLINE_HARD。此时应用的连接并不会全部中断,已经建立到故障节点连接的新请求会在一瞬间失败,但下次代理重新建立连接时会自动绕开故障节点。在实际业务中,连接池内的空闲连接会被 ProxySQL 主动回收,所以应用侧几乎无感知。
如果节点宕机时恰好有未提交的写事务,这个事务会直接报错回滚,应用需要做好重试机制。这一步是 PXC + ProxySQL 架构无法完全规避的,也是任何高可用方案都必须接受的事实。
5.2 节点重启后是走 IST 还是 SST
把宕机的 12 节点重新启动,它会尝试重新加入集群。这里最值得关心的是数据追赶方式。
如果节点掉线期间,集群产生的写入量没有超过 Donor 节点 gcache 中缓存的写集大小,节点会通过 IST(增量状态传输)快速追赶,通常几秒到几十秒就能回到 Synced 状态。如果掉线时间较长或写入量特别大,缺失的数据集已经超过 gcache 容量,节点就只能通过 SST(全量状态传输)从 Donor 节点拉取整个数据目录的物理备份,这个时间取决于数据量,从几分钟到几十分钟不等。
我这次演练中,因为 gcache.size 配置了 1G,而测试库的繁忙程度不高,所以节点重连后走的是 IST 增量同步,日志里能看到类似 “State transfer required” 但很快完成。如果你的节点每次重连都触发 SST,优先检查 gcache 参数是否偏小。
5.3 集群全部宕机后的启动顺序与 safe_to_bootstrap
最严格也是最容易出错的场景,是整个集群所有节点同时断电重启。此时不能直接在每个节点上盲目执行 systemctl start mysql,因为每个节点都试图与其他节点建立连接,谁也不知道哪个节点来做引导更合适。
PXC 在启动流程里用 safe_to_bootstrap 标记来指定引导节点。默认只有一台节点的 /var/lib/mysql/grastate.dat 文件里 safe_to_bootstrap 是 1,这台节点就是上次正常关闭时数据最完整的节点。
正确的恢复流程是:
- 在刚才指定的引导节点上执行 galera_new_cluster,启动完整的集群。
- 等引导节点进入 Primary 状态。
- 再在其余节点上执行 systemctl start mysql。
如果主引导节点硬件彻底损坏,需要手动选择另一台数据最新的节点。方法是先看所有存活节点中谁的事务序列号最大,然后修改该节点的 grastate.dat 文件,把 safe_to_bootstrap 改为 1,再执行引导。这个过程需要对数据有绝对把握,任何误设都会导致数据分裂。
5.4 网络分区和脑裂保护是怎么工作的
传统主从架构里最让人头疼的就是脑裂:主库和从库之间的网络中断,切到从库后,旧主库又恢复,两边同时接受写入,最后数据全乱。PXC 从根本上杜绝了这个问题。
Galera 使用 quorum 机制,当节点间的网络分区发生时,每个节点会自行判断自己所在分区是否拥有超过半数的节点。少数派分区的节点会把自己置为非 Primary 状态,对外表现为主机拒绝执行任何 DML 语句,只能做只读或干脆拒绝连接;只有多数派分区继续工作。这样就不会出现两个分区同时写入造成的数据分裂。
ProxySQL 在感知到节点处于非 Primary 状态后,会自动把该节点从可用列表中摘除。所以对整个系统来说,网络抖动时实际上是一个自动化的“降级+恢复”过程。
6. 半年运行后值得分享的调优和填坑清单
6.1 gcache 尺寸决定重逢时是“增量追”还是“全量追”
gcache 的大小直接决定了节点掉线后恢复方式的代价。默认的 128MB 在很多业务下都偏小,只要写流量稍微大一点,节点掉线几分钟就可能触发全量 SST。我建议生产环境的 gcache.size 至少设置为 1G,如果单日 binlog 增长量超过 50G,建议直接上 4G。
这个参数在 wsrep_provider_options 里配置,修改后需要重启 MySQL 才能生效:
wsrep_provider_options="gcache.size=4G; gcache.page_size=128M; gcache.mem_size=256M"gcache.page_size 是写集缓存的页大小,通常保持默认值就行,除非你明确遇到性能问题。
6.2 ProxySQL 的常见坑:认证插件、字符集、配置持久化
ProxySQL 接入 MySQL 8.0 时,最常见的认证问题是 caching_sha2_password 插件不兼容。解决思路有两种:升级 ProxySQL 到支持该认证的新版本,或者在 MySQL 端创建账号时显式指定 mysql_native_password。我选用后者,因为改动面最小,适合存量业务快速切换。
第二个坑是字符集不一致。业务端连接 ProxySQL 后,如果客户端默认字符集和 PXC 节点的字符集不一致,可能出现乱码或建连失败。这时需要在 ProxySQL 管理端统一设置字符集:
SET mysql-default_charset='utf8mb4'; LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;第三个坑就是 4.4 节说的 LOAD TO RUNTIME 和 SAVE TO DISK 双命令,二者缺一不可。很多人在管理端改完配置后只做了 SAVE TO DISK,却忘了 LOAD TO RUNTIME,结果配置一直没生效。
6.3 备份、监控和日常巡检建议
PXC 集群自身具备多副本,但不能因此免除备份。我的备份方案是每个凌晨用 Percona XtraBackup 从某一个节点做全量物理备份,备份时加上 --galera-info 参数,确保备份集里带上集群的一致点信息。
监控层面,我重点盯几个关键指标:
- wsrep_flow_control_paused:如果持续大于 0,说明集群正在进行流量控制,某个节点 apply 速度跟不上写入速度,这是性能瓶颈的早期信号。
- wsrep_local_recv_queue:接收队列长时间堆积,说明从库节点处理不过来。
- wsrep_cluster_size 和 wsrep_cluster_status:直接反映集群是否存在节点掉线或非 Primary 状态。
ProxySQL 侧则通过 monitor.mysql_server_connect_log 和 monitor.mysql_server_ping_log 表查看历史健康检查记录,一旦出现异常摘除或恢复,这些日志能帮你快速定位时间线和根因。
日常巡检时,我还习惯定期在 ProxySQL 管理端检查 stats_mysql_query_digest,看有没有大量 SQL 由于路由规则不当被错误分发到写组。结合慢查询日志,很容易发现规则正则还需要哪些微调。
做了这么多轮调整之后,我最大的体会是:PXC + ProxySQL 并不是一个“装完就万事大吉”的方案,它需要你用观测、演练和参数调优去持续喂养。但一旦把地基打好,深夜被数据库故障叫醒的概率,真的能降到很低。如果你正在考虑从传统主从架构迁过来,建议先把第 2 章里的限制清单过一遍,确认业务兼容后再动手,整套改造的收益会远超你的预期。