1. 项目概述:为什么需要这套组合拳来扛住数据库流量洪峰
在某次支撑某高校在线选课系统的压测中,我亲眼见过单台MySQL实例在峰值时段每秒被砸进3200+个连接请求,CPU直接飙到98%,慢查询日志里堆满了超过5秒的SELECT语句。那一刻我就意识到:光靠调优参数、加索引、读写分离,已经解决不了根本问题——数据库层的可用性瓶颈,必须从架构层面破局。而标题里这个“Haproxy+Keepalived+Percona XtraDB Cluster”组合,不是随便拼凑的网红方案,它是经过真实业务压力反复锤炼后,留下来的一套兼顾强一致性、自动故障转移、无单点风险、且运维路径清晰的高可用落地范式。
核心关键词“Haproxy”“Keepalived”“Percona XtraDB Cluster”背后,其实对应着三层防御体系:PXC负责数据层的多节点同步与事务一致性(本质是Galera集群),Haproxy承担七层流量分发与健康检查,Keepalived则在最外层兜底,提供虚拟IP漂移能力,确保客户端永远连得上那个“活着的入口”。这三者不是并列关系,而是有明确职责边界的上下游协作链路——PXC是心脏,Haproxy是神经中枢,Keepalived是呼吸反射弧。很多团队部署失败,根源就在于没理清这个逻辑:比如把Haproxy健康检查直接指向PXC节点的3306端口,却忽略了PXC自身状态(如wsrep_local_state=4才代表完全同步),结果主节点挂了,Haproxy还在往一个半同步状态的节点转发流量,导致事务丢失;又或者Keepalived的vrrp_script脚本只检测Haproxy进程是否存在,却不校验其后端真实可用节点数,造成VIP漂移后流量全部打到一个已失联的PXC节点上。
这套方案真正解决的是三个刚性需求:第一,RTO(恢复时间目标)控制在10秒内——当任意PXC节点宕机,Haproxy在3秒内摘除该节点,Keepalived在7秒内完成VIP切换;第二,RPO(恢复点目标)为0——PXC基于全同步复制,所有已提交事务必然在至少两个节点上落盘;第三,运维可预期——所有组件配置项均可通过文本文件管理,故障排查路径清晰(从VIP→Haproxy日志→PXC状态变量),不像某些商业中间件,出问题只能等厂商支持。它适合中大型业务系统,尤其是对数据强一致有硬性要求的场景,比如金融类交易、教务成绩管理、医疗检验报告生成等。如果你的系统还停留在主从复制+手动切库阶段,那这套方案就是你技术债清单上最该优先偿还的一笔。
2. 整体架构设计与组件选型逻辑:为什么是它们,而不是别的
2.1 PXC为何不可替代:强一致性的底层基石
很多人问:为什么不用MySQL Group Replication(MGR)?或者直接上ProxySQL+原生主从?这里必须掰开揉碎讲清楚。PXC(Percona XtraDB Cluster)的核心竞争力,在于它把Galera库深度集成进MySQL内核,实现了真正的同步复制(Synchronous Replication)。注意,这不是MySQL官方MGR那种“最终一致性”的准同步,而是每个事务在提交前,必须获得集群中多数派节点(quorum)的确认,才会返回客户端成功。举个例子:一个3节点PXC集群,节点A发起INSERT,它会把事务写入本地redo log,同时将binlog event广播给B和C;只有当B和C都成功应用该event并返回ACK,A才允许commit。这个过程耗时约15~30ms(实测值),但换来的是绝对的数据零丢失。
反观MGR,它采用的是基于Paxos的异步共识机制,虽然也支持单主模式,但存在“写入延迟窗口”——主节点写入成功后立即返回客户端,再异步通知从节点apply。如果此时主节点突然宕机,而从节点还没来得及拉取到最新binlog,就会产生数据不一致。我们曾用sysbench做对比测试:在模拟网络分区场景下,MGR集群出现过12条记录的丢失,而PXC全程零差异。至于ProxySQL+主从,它本质上还是异步复制,RPO永远大于0,且故障切换需依赖外部脚本判断,RTO通常在30秒以上。所以,当你看到标题里明确写着“Percona XtraDB Cluster”,就等于锁定了强一致这个最高优先级目标,其他方案在此前提下自动出局。
2.2 Haproxy为何比Nginx更适配数据库负载:七层感知的精度差异
选择Haproxy而非Nginx做数据库代理,关键在于健康检查的颗粒度。Nginx的upstream健康检查仅能探测TCP端口是否可达(即3306端口有没有监听),但它无法理解MySQL协议,更无法识别PXC节点的真实状态。而Haproxy可以通过自定义脚本(option httpchk + check)深入到PXC的wsrep状态变量层。我们实际部署时,Haproxy的check脚本会执行如下SQL:
SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME='wsrep_local_state';只有当返回值为4(Joined,即完全同步状态)时,Haproxy才认为该节点健康。如果返回2(Joiner,正在同步中)或1(Donor,正在给其他节点传输数据),Haproxy会立即将其标记为DOWN,并停止转发新连接。这个能力Nginx原生不支持,必须依赖第三方模块或复杂Lua脚本,稳定性存疑。另外,Haproxy的连接池管理更精细——它支持balance leastconn(最小连接数调度),在PXC这种写操作相对均衡的场景下,比Nginx的轮询(round-robin)更能避免单节点过载。我们压测时发现,当3节点PXC承载8000QPS写入时,Nginx轮询会导致某节点连接数峰值达1200,而Haproxy leastconn下各节点连接数稳定在750±30,CPU利用率波动小了40%。
2.3 Keepalived的不可替代性:虚拟IP是最后的保命符
有人质疑:既然Haproxy本身支持多实例,为什么还要加Keepalived?答案是故障隔离域不同。Haproxy解决的是“后端数据库节点失效”问题,而Keepalived解决的是“Haproxy自身失效”问题。设想这样一个场景:Haproxy所在服务器因内核panic重启,整个服务中断。如果没有Keepalived,客户端必须修改连接字符串指向备用Haproxy IP,这在微服务架构中几乎不可能——成百上千个服务实例不可能实时更新配置。而Keepalived通过VRRP协议,在两台Haproxy服务器间维护一个浮动VIP(如192.168.10.100)。当主Haproxy宕机,备机在3秒内接管VIP,客户端连接完全无感。这里有个关键细节:Keepalived的vrrp_script不能只检测Haproxy进程,必须叠加PXC健康度校验。我们的生产脚本是这样写的:
#!/bin/bash # /etc/keepalived/check_haproxy.sh if [ $(ps -C haproxy --no-header | wc -l) -eq 0 ]; then exit 1 fi # 检查Haproxy后端健康节点数是否≥2 if [ $(echo "show stat" | socat stdio unix:/var/run/haproxy.sock | \ awk -F',' '$1=="stats" && $18>0 {print $18}' | wc -l) -lt 2 ]; then exit 1 fi exit 0这个脚本确保:只有当Haproxy进程存活,且其监控到的PXC健康节点不少于2个时,Keepalived才认为主节点有效。否则强制触发VIP漂移。这就把数据库层的可用性保障,从单点延伸到了整个访问链路。
3. 核心组件部署与配置详解:从零开始搭建可落地的环境
3.1 环境准备与基础依赖安装:避开那些坑人的系统级陷阱
部署前必须统一三台服务器的基础环境,这是后续所有配置稳定的前提。我们选用CentOS 7.9(内核3.10.0-1160),所有节点时间必须严格同步(NTP),否则PXC节点间会因时间戳校验失败拒绝加入集群。具体步骤如下:
首先关闭SELinux(PXC官方明确不兼容SELinux):
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config setenforce 0然后调整系统参数,重点是文件描述符和网络缓冲区。PXC对并发连接极其敏感,ulimit -n必须设为65535以上:
echo "* soft nofile 65535" >> /etc/security/limits.conf echo "* hard nofile 65535" >> /etc/security/limits.conf echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf echo "net.ipv4.tcp_max_syn_backlog = 65535" >> /etc/sysctl.conf sysctl -p提示:很多团队卡在第一步就是因为没改
somaxconn,导致PXC启动时日志报错“Failed to bind socket”,实际是内核队列满溢,而非端口被占。
接着安装EPEL源和基础工具:
yum install epel-release -y yum install socat wget curl vim net-tools -y特别注意:不要用yum直接装percona-xtradb-cluster!CentOS 7默认仓库里的版本太老(5.7.x),存在已知的wsrep状态同步bug。必须从Percona官网下载最新版(当前稳定版为8.0.33-25.1),并使用rpm包安装:
wget https://downloads.percona.com/downloads/Percona-XtraDB-Cluster-LATEST/Percona-XtraDB-Cluster-8.0.33-25.1/binary/redhat/7/x86_64/percona-xtradb-cluster-80-8.0.33-25.1.el7.x86_64.rpm rpm -ivh percona-xtradb-cluster-80-8.0.33-25.1.el7.x86_64.rpm安装完成后,先别急着启动,要检查MySQL用户权限。PXC要求mysql用户对/var/lib/mysql目录有完全控制权,且不能被systemd的PrivateTmp机制隔离。编辑/usr/lib/systemd/system/mysqld.service,注释掉PrivateTmp=true这一行,然后重载配置:
systemctl daemon-reload3.2 Percona XtraDB Cluster节点配置:三个节点的差异化设置要点
PXC集群中,第一个节点(Bootstrap节点)的启动方式与其他节点完全不同,这是最容易出错的环节。所有节点共用同一份/etc/my.cnf,但需根据角色微调。核心配置段如下:
[mysqld] # 基础配置 server-id=101 datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid # PXC特有配置 wsrep_provider=/usr/lib64/galera3/libgalera_smm.so wsrep_cluster_address=gcomm://192.168.10.101,192.168.10.102,192.168.10.103 wsrep_node_address=192.168.10.101 wsrep_node_name=node1 wsrep_cluster_name=pxc-cluster wsrep_sst_method=xtrabackup-v2 wsrep_sst_auth=sst:your_secure_password关键参数解析:
wsrep_cluster_address:必须写全所有节点IP,用英文逗号分隔,不能包含空格。这是集群发现的唯一依据。wsrep_node_address:必须填本机真实IP(非127.0.0.1),且需确保该IP能被其他节点路由访问。wsrep_sst_method:选择xtrabackup-v2而非rsync,因为前者支持热备份,集群扩容时不会锁表。wsrep_sst_auth:SST(State Snapshot Transfer)认证账号,需提前在MySQL中创建:CREATE USER 'sst'@'localhost' IDENTIFIED BY 'your_secure_password'; GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'sst'@'localhost'; FLUSH PRIVILEGES;
注意:三个节点的
wsrep_node_address和wsrep_node_name必须唯一,且server-id不能重复(建议用IP后三位,如101/102/103)。启动顺序至关重要:先启动Bootstrap节点,再依次启动其他节点。Bootstrap节点启动命令特殊:
# 节点1(192.168.10.101)执行 /etc/init.d/mysql bootstrap-pxc # 节点2和3(192.168.10.102/103)执行标准启动 systemctl start mysqld启动后,登录任一节点执行SHOW STATUS LIKE 'wsrep_%';,重点检查:
wsrep_local_state_comment应为Syncedwsrep_cluster_size应为3wsrep_ready应为ON
如果wsrep_cluster_size始终为1,大概率是防火墙阻断了4567(GCS通信端口)或4568(IST端口)。用telnet 192.168.10.102 4567逐个测试连通性。
3.3 Haproxy代理层配置:让流量聪明地找到健康的PXC节点
Haproxy配置文件/etc/haproxy/haproxy.cfg需分为frontend(接入层)、backend(后端池)、listen(监控页)三部分。我们采用TCP模式(mode tcp)而非HTTP,因为MySQL是二进制协议,HTTP模式会解析失败:
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /var/run/haproxy.sock mode 660 level admin expose-fd listeners user haproxy group haproxy daemon defaults mode tcp timeout connect 5000ms timeout client 50000ms timeout server 50000ms option httpchk GET /health HTTP/1.0\r\nHost:\ www http-check expect status 200 # 数据库读写分离前端(所有流量走此入口) frontend db_frontend bind *:3307 default_backend pxc_backend # PXC后端集群(3个节点,带健康检查) backend pxc_backend balance leastconn option httpchk GET /health HTTP/1.0\r\nHost:\ www http-check expect status 200 # 自定义健康检查脚本路径 option httpchk http-check send hdr Host www http-check expect status 200 # 后端节点定义 server node1 192.168.10.101:3306 check port 8080 inter 2000 rise 3 fall 3 server node2 192.168.10.102:3306 check port 8080 inter 2000 rise 3 fall 3 server node3 192.168.10.103:3306 check port 8080 inter 2000 rise 3 fall 3 # 监控页面(便于实时查看节点状态) listen stats bind *:8080 mode http stats enable stats uri /stats stats auth admin:password123这里的关键在于check port 8080——Haproxy会向每个PXC节点的8080端口发送HTTP请求,而这个端口由我们部署的轻量级健康检查服务监听。在每台PXC节点上,创建/var/www/html/health脚本:
#!/bin/bash # /var/www/html/health STATE=$(mysql -ustats -pstats123 -e "SHOW STATUS LIKE 'wsrep_local_state';" 2>/dev/null | tail -1 | awk '{print $2}') if [ "$STATE" = "4" ]; then echo "HTTP/1.1 200 OK" echo "Content-Type: text/plain" echo "" echo "OK" else echo "HTTP/1.1 503 Service Unavailable" echo "Content-Type: text/plain" echo "" echo "Node not synced" fi然后用Python起一个极简HTTP服务(无需安装nginx):
python3 -m http.server 8080 --directory /var/www/html &实操心得:很多团队用curl直接调MySQL检查,结果Haproxy日志疯狂刷
Connection refused。这是因为MySQL默认不监听8080端口,必须单独起服务。我们用Python内置server,5行代码搞定,稳定运行两年零故障。
3.4 Keepalived高可用网关配置:让VIP在故障时精准漂移
Keepalived配置/etc/keepalived/keepalived.conf需区分主备节点。以主节点(192.168.10.101)为例:
! Configuration File for keepalived global_defs { router_id LVS_DEVEL } vrrp_script chk_haproxy { script "/etc/keepalived/check_haproxy.sh" interval 2 weight 2 fall 2 rise 1 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.10.100/24 } track_script { chk_haproxy } }备节点(192.168.10.102)只需修改三处:
state改为BACKUPpriority改为90(必须低于主节点)virtual_router_id必须与主节点一致(这是VRRP组标识)
注意:
interface eth0必须填服务器实际网卡名,用ip addr命令确认。曾经有团队填成ens33,结果VIP绑定失败,排查了两天才发现网卡名不匹配。
启动顺序严格遵循:先启PXC集群 → 再启Haproxy → 最后启Keepalived。验证VIP是否生效:
# 在主节点执行 ip addr | grep 192.168.10.100 # 应输出类似:inet 192.168.10.100/24 scope global secondary eth0 # 在客户端测试连接 mysql -h192.168.10.100 -P3307 -ustats -pstats123 -e "SELECT @@hostname;" # 应返回当前处理请求的PXC节点名(如node1)4. 高可用验证与故障模拟:亲手撕开方案的可靠性底裤
4.1 正常流量下的性能基线测试:建立可信的参照系
在模拟故障前,必须先建立性能基线。我们用sysbench 1.0.20对PXC集群进行压测,脚本如下:
sysbench oltp_read_write \ --db-driver=mysql \ --mysql-host=192.168.10.100 \ # 指向VIP --mysql-port=3307 \ --mysql-user=stats \ --mysql-password=stats123 \ --mysql-db=sbtest \ --tables=10 \ --table-size=100000 \ --threads=128 \ --time=300 \ --report-interval=10 \ run实测结果(3节点PXC+Haproxy+Keepalived):
| 指标 | 数值 | 说明 |
|---|---|---|
| TPS(每秒事务数) | 1842 | 稳定在1800±50区间,无抖动 |
| 平均响应时间 | 69ms | 95%分位<120ms |
| 连接数峰值 | 1120 | Haproxy显示各后端节点连接数均衡(370/375/375) |
| CPU利用率 | 节点1:62%, 节点2:58%, 节点3:60% | 无明显倾斜 |
这个基线意味着:当VIP流量稳定在1800TPS时,整套架构处于健康水位。任何故障后的性能衰减,都以此为锚点评估。
4.2 主动制造故障并观测恢复过程:四次关键演练记录
故障1:PXC节点宕机(模拟磁盘损坏)
操作:在节点2上执行kill -9 $(pgrep mysqld)
观测:
- Haproxy日志在2秒内出现
Server pxc_backend/node2 is DOWN show stat监控页中node2状态变红- 客户端持续收到响应,TPS短暂跌至1620(-12%),10秒后回升至1780
SHOW STATUS LIKE 'wsrep_cluster_size'返回2
结论:Haproxy自动摘除故障节点,流量重新分配,RTO≈2秒,RPO=0
故障2:Haproxy主进程崩溃
操作:kill -9 $(pgrep haproxy)
观测:
- Keepalived日志显示
VRRP_Instance(VI_1) Entering FAULT STATE - 3秒后备机日志出现
VRRP_Instance(VI_1) Entering MASTER STATE - 客户端
ping 192.168.10.100持续通,mysql -h192.168.10.100连接成功 - 新建连接被导向备机Haproxy,TPS无下降
结论:Keepalived接管VIP,RTO≈3秒,业务无感知
故障3:网络分区(模拟交换机故障)
操作:在节点1上执行iptables -A INPUT -s 192.168.10.102 -j DROP
观测:
- 节点1的
wsrep_incoming_addresses中消失节点2地址 wsrep_cluster_conf_id在节点1和节点2/3间分裂为两个值- Haproxy因健康检查失败,将节点2标记为DOWN
- 节点1独立形成单节点集群(
wsrep_cluster_size=1),但Haproxy已将其隔离,流量只流向节点2/3
结论:网络分区时,Haproxy的健康检查成为安全阀,阻止了脑裂流量
故障4:Keepalived主备倒换(模拟主机房断电)
操作:直接断开主节点电源
观测:
- 备机在7秒内完成VIP绑定(
ip addr可见192.168.10.100) - 客户端
telnet 192.168.10.100 3307立即成功 - 所有连接重建,TPS在15秒内恢复至基线水平
结论:物理层故障下,VIP漂移可靠,RTO<10秒
4.3 日常巡检清单:五条命令守住生产环境生命线
在真实运维中,我们每天早9点执行以下检查,10分钟内完成全链路健康扫描:
VIP连通性
ping -c 3 192.168.10.100 && echo "VIP可达" || echo "VIP异常"Haproxy后端状态
echo "show stat" | socat stdio unix:/var/run/haproxy.sock | \ awk -F',' '$1=="pxc_backend" {print $1,$18,$19}' | column -t # 输出示例:pxc_backend node1 1 UP node2 0 DOWN node3 1 UPPXC集群状态
mysql -h192.168.10.100 -P3307 -e "SHOW STATUS LIKE 'wsrep_%';" | \ awk '/wsrep_cluster_size|wsrep_ready|wsrep_local_state/ {print}' # 必须满足:cluster_size≥2, ready=ON, local_state=4Keepalived主备状态
ip addr | grep 192.168.10.100 && echo "MASTER运行中" || echo "当前为BACKUP"慢查询积压
mysql -h192.168.10.100 -P3307 -e "SELECT COUNT(*) FROM information_schema.PROCESSLIST WHERE TIME > 30;" # 超过30秒的连接数应为0
踩过的坑:曾因忘记检查
wsrep_ready,导致节点处于ON但实际同步中断(日志显示WSREP: Member 2.0 (node2) requested state transfer),结果Haproxy还在转发流量,引发大量超时。现在这条检查已固化为告警规则。
5. 常见问题与根因排查:那些文档里不会写的实战经验
5.1 PXC节点无法加入集群:九成问题出在这三个地方
问题现象:新节点启动后,SHOW STATUS LIKE 'wsrep_%';中wsrep_cluster_size始终为1,日志反复打印WSREP: Failed to prepare for incremental state transfer
根因1:SST认证失败(占比65%)
检查/var/log/mysqld.log,若出现Access denied for user 'sst'@'localhost',说明wsrep_sst_auth配置的账号密码与MySQL中创建的不一致。解决方案:
- 登录已有节点,执行
SELECT User,Host FROM mysql.user WHERE User='sst'; - 确认密码正确性,必要时重置:
ALTER USER 'sst'@'localhost' IDENTIFIED BY 'new_password'; - 修改
/etc/my.cnf中的wsrep_sst_auth=sst:new_password
根因2:防火墙阻断GCS端口(占比25%)
PXC使用4567端口进行集群状态同步(GCS),4568端口用于增量状态传输(IST)。用以下命令验证:
# 在新节点执行(替换为目标IP) nc -zv 192.168.10.101 4567 && echo "4567通" || echo "4567不通" nc -zv 192.168.10.101 4568 && echo "4568通" || echo "4568不通"若不通,临时关闭防火墙测试:systemctl stop firewalld。确认后开放端口:
firewall-cmd --permanent --add-port=4567/tcp firewall-cmd --permanent --add-port=4568/tcp firewall-cmd --reload根因3:磁盘空间不足(占比10%)
SST过程会生成临时快照文件,要求/tmp目录至少有2GB空闲。检查命令:
df -h /tmp # 若空间不足,修改SST临时目录: # 在my.cnf中添加 wsrep_tmpdir=/data/tmp (需提前创建并授权)5.2 Haproxy连接数暴涨且无法释放:真相是MySQL连接池未配置
问题现象:Haproxy监控页显示pxc_backend连接数持续攀升至5000+,netstat -an | grep :3306 | wc -l查看MySQL实际连接数却只有200,大量连接处于TIME_WAIT状态
根因:Haproxy默认启用option http-server-close,但MySQL客户端(如JDBC)未配置连接池,每次请求都新建TCP连接,Haproxy无法复用。解决方案分两步:
Step1:Haproxy端优化
在backend段添加:
option tcp-check tcp-check connect tcp-check send "SELECT 1;\n" tcp-check expect string "1" timeout server 30sStep2:应用端强制连接池
以Java Spring Boot为例,在application.yml中:
spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000实测效果:连接数从5000+降至稳定在300左右,TIME_WAIT数量减少90%。
5.3 Keepalived VIP漂移后客户端连接失败:被忽略的ARP缓存
问题现象:Keepalived完成VIP漂移后,部分客户端(尤其是Linux服务器)仍尝试连接旧主机,tcpdump抓包显示ARP请求发向原MAC地址
根因:客户端操作系统ARP缓存未刷新。Linux默认ARP缓存超时为30秒,Windows为2分钟。解决方案:
临时修复(客户端执行):
# Linux ip neigh flush dev eth0 192.168.10.100 # Windows arp -d 192.168.10.100永久修复(服务端配置):
在Keepalived配置的vrrp_instance段添加:
notify_master "/etc/keepalived/notify.sh"创建/etc/keepalived/notify.sh:
#!/bin/bash if [ "$1" = "MASTER" ]; then # 强制刷新ARP表 arping -U -c 3 -I eth0 192.168.10.100 fi经验总结:这个ARP问题在Kubernetes环境中尤为突出,因为Pod的网络栈会缓存ARP。我们在Ingress Controller中集成此脚本后,VIP漂移成功率从82%提升至100%。
6. 方案演进与边界思考:这套架构还能走多远
这套Haproxy+Keepalived+PXC方案,在我们支撑的23个业务系统中稳定运行了4年,但它并非银弹。随着业务发展,我们开始直面它的能力边界,并探索演进路径。
边界1:写入吞吐量天花板
PXC的同步复制机制决定了其写入性能天然弱于单机MySQL。实测数据显示,当集群规模从3节点扩展到5节点时,TPS反而下降18%——因为事务需获得4个节点的ACK,网络延迟成为瓶颈。对于日均写入超5亿条的物联网平台,我们已将PXC降级为“强一致性核心库”,将海量设备上报数据分流至TimescaleDB时序库,仅用PXC存储设备元数据和指令下发记录。这种混合架构,既保住了关键数据的一致性,又突破了写入瓶颈。
边界2:跨机房部署的挑战
PXC要求所有节点网络延迟<5ms,否则同步延迟剧增。当业务要求双活数据中心(如北京+上海)时,PXC无法直接部署。我们的解法是:在北京机房部署完整PXC集群(3节点),上海机房部署只读从库(MySQL原生主从),通过Canal订阅PXC的binlog,异步同步到上海。虽然RPO变为分钟级,但满足了两地用户就近读取的需求。这里的关键是,Haproxy的frontend需拆分为read_write和read_only两个入口,由应用层按业务类型路由。
边界3:云原生环境的适配
在Kubernetes中部署这套方案,最大的障碍是Keepalived的VIP机制与Service模型冲突。我们的实践是:弃用Keepalived,改用MetalLB作为裸机负载均衡器,将Haproxy暴露为LoadBalancer类型Service;PXC则用StatefulSet部署,通过Headless Service实现DNS发现。Haproxy的健康检查脚本需改造为K8s readiness probe,直接调用PXC Pod的/health端点。这套云原生改造,使集群部署时间从2小时缩短至8分钟,且天然支持滚动升级。
最后分享一个血泪教训:某次大促前,运维同事为“提升性能”将Haproxy的timeout server从50秒改为5秒。结果在PXC执行大事务(如DDL)时,Haproxy误判为超时,主动断开连接,导致事务回滚。后来我们约定:所有超时参数必须基于PXC最慢操作(如ALTER TABLE)的实测耗时设定,留足2倍余量。技术方案的价值,不在于纸面参数多漂亮,