业务量一涨,MySQL最先撑不住的往往是读请求。一台主库又写又读,CPU和IO很快就会打满,这时候最直接的解法就是把读和写拆开:写走主库,读走从库,多个从库之间再做负载均衡。但光有读写分离还不够,如果负责接收请求的那台数据库挂了,整个服务就中断了,所以还需要Keepalived这样的高可用组件做故障自动切换。这篇文章就把读写负载均衡和高可用这两件事放在一起讲清楚。

读写分离的实现原理与架构选型
读写分离的本质是让写请求和读请求走不同的数据库实例。MySQL本身通过主从复制把主库的数据异步同步到从库,这是读写分离的基础。写操作只在主库执行,主库把变更写入binlog,从库的IO线程拉取binlog写入中继日志,再由SQL线程重放,这样从库的数据就和主库保持一致了。
实现读写分离有几种常见方式,各有适用场景。第一种是在应用层做,比如在代码里配置两个数据源,写操作用主库连接,读操作用从库连接,配合Sharding-JDBC或者Spring的AbstractRoutingDataSource都能实现。这种方式性能最好,没有中间层损耗,但代码侵入性强,应用需要感知主从的角色。第二种是中间代理层,比如MyCat、ProxySQL、MySQL Router,应用只连代理,代理根据SQL语句类型自动路由,这种方式对应用透明,但多了一层网络转发,链路变长,排错也复杂一些。
本文采用第三种思路:MySQL双主架构加Keepalived虚拟IP。两台数据库互为主从,任意一台写入的数据都会复制到对方,外面通过一个虚拟IP(VIP)对外提供服务,Keepalived负责把VIP漂移到健康的那台机器上。这种方案组件少、结构清晰,读写都走VIP,配合前面说的应用层读写分离,从库直接暴露真实IP承接读流量,主库的VIP只承接写流量,整体架构既简单又可靠。
双主复制架构搭建
假设两台服务器,192.168.10.11和192.168.10.12,系统都装好MySQL。双主的核心是两台都开启binlog,并且设置不同的server-id,同时配置自增偏移,防止两边同时写入时主键冲突。
# 两台机器都要修改 /etc/my.cnf 的 [mysqld] 段 # 机器A 192.168.10.11 [mysqld] server-id = 1 log-bin = mysql-bin binlog_format = ROW auto_increment_offset = 1 auto_increment_increment = 2 # 机器B 192.168.10.12 # server-id = 2,其余相同,auto_increment_offset = 2
配置完重启MySQL后,建立复制账号并互相指定为主库。在机器A上执行:
CREATE USER 'repl'@'192.168.10.%' IDENTIFIED BY 'Repl@123456'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.10.%'; FLUSH PRIVILEGES; -- 查看A的binlog位置 SHOW MASTER STATUS; -- 在机器B上执行,指向A CHANGE MASTER TO MASTER_HOST='192.168.10.11', MASTER_USER='repl', MASTER_PASSWORD='Repl@123456', MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=157; START SLAVE; -- 然后反向在A上同样操作指向B
两边都执行SHOW SLAVE STATUS\G,确认Slave_IO_Running和Slave_SQL_Running都是Yes,双主复制就通了。这里要强调一点,双主架构下建议同一时间只让一个节点接受写入,另一个节点主要作为热备和读库,否则双写虽然因为自增偏移不会主键冲突,但业务逻辑上的冲突很难避免。
Keepalived安装与配置文件详解
Keepalived基于VRRP协议工作,简单说就是多台服务器组成一个虚拟路由冗余组,对外呈现一个虚拟IP,组内通过优先级选举MASTER和BACKUP,MASTER持有VIP并定期发送通告报文,一旦BACKUP收不到通告就接管VIP,对客户端来说IP从未变化,服务自然不中断。
在两台机器上安装Keepalived,以CentOS为例,yum install -y keepalived即可。核心配置文件在/etc/keepalived/keepalived.conf,下面是机器A作为MASTER的完整配置:
vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh"
interval 3
weight -30
fall 2
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass MySecret
}
virtual_ipaddress {
192.168.10.100/24
}
track_script {
chk_mysql
}
}机器B的配置基本相同,只有两处区别:state改为BACKUP,priority改为90。这里逐个解释关键参数。virtual_router_id是VRRP组的标识,两台必须一致,而且同网段内不能和其他Keepalived集群的ID冲突。priority决定谁当MASTER,数字越大优先级越高。vrrp_script定义了一个健康检查脚本,每3秒执行一次,检测失败两次就扣30分权重,一旦A的优先级掉到比B低,VIP就会漂移到B,这就是自动故障切换的触发机制。
检测脚本的内容很简单,判断mysqld进程是否存活,不存活就返回非零:
#!/bin/bash
if ! pidof mysqld >/dev/null; then
systemctl stop keepalived
exit 1
fi
exit 0注意脚本里用了直接停掉Keepalived的方式,这比单纯靠权重扣分更干脆,能确保VIP快速漂移。写完记得chmod +x加上执行权限,否则脚本跑不起来,健康检查会一直失败导致VIP来回漂移。
故障切换验证与常见问题处理
配置完成后,在两台机器上分别执行systemctl start keepalived,然后用ip addr查看,MASTER那台的网卡上应该能看到192.168.10.100这个VIP。验证切换很简单,开一个客户端持续连接VIP执行查询,然后手动停掉机器A的MySQL:systemctl stop mysqld,正常情况下几秒内VIP会漂到机器B,客户端断开重连后继续可用。
有几个实际部署中容易踩的坑值得单独说。第一个是脑裂问题:如果两台服务器之间的心跳通信被防火墙挡了,双方都认为对方挂了,都宣称自己是MASTER,VIP出现在两台机器上,导致IP冲突、数据错乱。解决办法一是确认VRRP协议的112端口在两机之间放通,二是可以在检测脚本里加仲裁逻辑,比如ping网关,ping不通网关就主动放弃MASTER身份。
第二个是主从延迟。读写分离后,客户端刚写入一条数据马上读,可能读到的是从库的旧数据。这在架构上要区分对待:强一致要求的读操作强制走主库,允许轻微延迟的报表类查询走从库。同时从库可以开半同步复制,降低数据丢失风险。
-- 主库安装半同步插件 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; SET GLOBAL rpl_semi_sync_master_enabled = 1; SET GLOBAL rpl_semi_sync_master_timeout = 3000;
第三个是切换后的数据补偿。Keepalived只负责IP漂移,不负责数据层面的处理,如果故障时还有 binlog 没复制完,新主库可能缺最后一小段数据。生产环境建议配合MHA或者Orchestrator这类工具做补日志和角色切换,Keepalived解决可用性,复制管理工具解决数据完整性,两者配合才是完整的方案。整体来说,这套架构组件成熟、维护成本低,对中小规模的业务是很实用的选择。
MySQL读写分离Keepalived高可用负载均衡配置修改时间:2026-09-15 17:26:48