MySQL高可用架构选型:从故障切换耗时到数据一致性,三种方案实测对比
大家好,我是数据库小学妹 👋
上个月凌晨主库挂了,没有自动切换。我手动切了十五分钟,丢了好几万订单。
那天之后,我花两周把MHA、MGR、InnoDB Cluster都搭了一遍。今天把完整对比写出来。
先放一张三种方案的架构对比,帮你建立全局认知:
MHA架构 MGR单主架构 InnoDB Cluster架构
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Manager │(监控) │ Node A │(主) │ Router │(代理)
└────┬─────┘ │ Paxos │ └────┬─────┘
│监控 └──┬───┬───┘ │路由
┌────┴─────┐ ┌───┴┐ ┌┴───┐ ┌────┴─────┐
│ Master │(主) │Node│ │Node│ │ Shell │(管理)
└────┬─────┘ │ B │ │ C │ └────┬─────┘
│复制 └────┘ └────┘ │
┌────┴─────┐ 三节点Paxos共识 ┌───┴────┐
│ Slave×2 │(从) │MGR单主 │(复制)
└──────────┘ └───┬────┘
Manager单点决策 共识协议决策 │
VIP漂移切换 毫秒级选主 自动路由切换
故障切换的核心指标:RTO和RPO
先搞清楚两个数。
RTO,故障切换耗时。主库挂了多久业务能恢复。电商超过30秒就是事故,内部管理系统停几分钟问题不大。
RPO,数据丢失量。切换过程中丢了多少数据。RPO为零意味着一条都不能丢。
不同业务对这两个指标的要求差别很大。选方案之前,先跟业务方确认能接受多大的损失。
MHA:最成熟的"老将"
架构原理
MHA(Master High Availability)的思路很直接。一个Manager进程监控主库,发现主库挂了,就从从库里挑一个数据最新的提升为新主。然后把其他从库都切过去。
关键步骤是补日志。主库宕机时,从库之间可能数据不一致。MHA会对比所有从库的relay log,找出差异。然后把缺失的event补到新主上,尽量少丢数据。
但MHA不是基于共识协议,靠的是Manager单点决策。Manager挂了集群还能跑,但没法自动切换。选型的时候这个短板要认真考虑。
部署配置
MHA要装两部分:Manager节点和所有MySQL节点上的Node。
# Manager配置 /etc/mha/app1.cnf
[server default]
manager_workdir=/var/log/mha/app1
manager_log=/var/log/mha/app1/manager.log
user=mha
password=mha_pass
ssh_user=root
repl_user=repl
repl_password=repl_pass
ping_interval=3
master_ip_failover_script=/usr/local/bin/master_ip_failover
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
[server1]
hostname=192.168.1.101
port=3306
[server2]
hostname=192.168.1.102
port=3306
candidate_master=1
[server3]
hostname=192.168.1.103
port=3306
no_master=1
candidate_master=1表示这个从库优先被选为新主。no_master=1表示永远不选它当主库,通常用来标记备库或者资源差的节点。
部署完要跑检查:
# 检查SSH连通性
masterha_check_ssh --conf=/etc/mha/app1.cnf
# 检查复制状态
masterha_check_repl --conf=/etc/mha/app1.cnf
我第一次部署,SSH检查就报错了。原因是三台机器的ssh免密配的用户不一致,Manager用root连,但Node上配的是mha用户。改了半天才统一。
故障切换实测
MHA的故障切换分三步:检测主库不可用→选新主→VIP漂移。
检测靠的是ping_interval配置的间隔去尝试连接主库,默认3秒连不上就再试。连续失败次数够了才判定宕机。这个阈值可以调,但调太小容易误判,调太大切换就慢。
我测出来的平均切换时间是12秒左右,最慢一次18秒。慢的那次是因为从库relay log差了几百个event,补日志花了额外时间。
踩坑:SSH误判
这是我在MHA上遇到的最头疼的问题。
测试环境有一次,主库运行得好好的,Manager却判定它挂了,触发了一次切换。业务被迫中断了几秒。
排查发现,是测试环境的网络有抖动。Manager的SSH连接超时了,连续几次ping失败,就误判为宕机。
解决办法是改检测策略。把ping_interval调到5秒,同时加上TCP端口检测做双重验证。SSH检测作为辅助,不再作为唯一的判断依据。
# 加上secondary_check_script做双重验证
secondary_check_script=/usr/local/bin/masterha_secondary_check \
--port=3306 --user=mha --password=mha_pass
这个脚本会从另一个网络路径去检测主库,两边都失败才判定宕机。
MGR:基于Paxos的"新锐"
为什么Paxos能保证零丢失
MGR(MySQL Group Replication)是5.7.17引入的。底层用的Paxos协议。
传统主从复制是"发了就不管"。主库写完binlog就返回成功。MGR不一样,事务提交前要过半数节点确认。主库才给客户端返回成功。
假设三个节点A、B、C,A是主:
- A执行事务,写本地binlog
- A把binlog event发给B和C
- B和C收到后返回确认
- A收到至少一个确认(加上自己是两个,过半),提交事务
如果A这时候挂了,B和C中至少有一个有完整数据。所以能做到零丢失。
不过代价也明显。每次写入多了一轮网络往返。写入延迟从异步复制的0.5ms涨到了2-5ms。并发量一上去,延迟会叠加。
部署配置
MGR的配置比MHA复杂得多,每个节点都要配,而且引导节点和普通节点的配置有区别。
# 所有节点公共配置 my.cnf
[mysqld]
server_id=1 # 每个节点不同
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_bin=binlog
binlog_format=ROW
log_slave_updates=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
transaction_write_set_extraction=XXHASH64
# MGR核心参数
plugin_load_add='group_replication.so'
group_replication_group_name="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot=OFF
group_replication_local_address="192.168.1.101:33061"
group_replication_group_seeds="192.168.1.101:33061,192.168.1.102:33061,192.168.1.103:33061"
group_replication_single_primary_mode=ON
注意group_replication_group_name要所有节点一样,是集群的唯一标识。group_replication_local_address是节点间通信的地址,要和MySQL端口分开,用另一个端口。
引导第一个节点和加入节点的SQL不一样:
-- 第一个节点(引导节点)
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
-- 其他节点(加入节点)
START GROUP_REPLICATION;
我第一次部署,引导节点忘关group_replication_bootstrap_group了。后面每次重启这个节点都会创建新组,其他节点全掉线。排查了半天才反应过来。
流控:最容易被忽略的机制
MGR有个流控(Flow Control)机制。很多团队上生产之后才发现还有这个东西。
当从节点回放速度跟不上主节点写入速度时,MGR会主动减慢主节点。让从节点追上来。
监控里能看到这个参数:
SHOW STATUS LIKE 'group_replication_flow_control%';
这个参数控制的是认证队列的长度阈值。默认25000,超过就触发流控。
我在压测时遇到过这个问题。QPS到8000左右,流控突然触发,主库响应时间从2ms飙到200ms。排查时看到监控里certifier队列爆了,才意识到是流控在限速。
解决办法有两个:调大阈值,或者优化从节点的回放能力(比如开并行复制)。如果业务允许,也可以关闭流控,但那样从节点会严重落后。
-- 调大流控阈值(根据业务量调整)
SET GLOBAL group_replication_flow_control_certifier_threshold=50000;
生产环境建议先测清楚触发流控的QPS是多少,提前做好规划。
多主模式:看起来很美
MGR支持多主模式,所有节点都能写入。但我不推荐轻易用。
多主模式下,两个节点同时修改同一行会冲突。MGR会回滚其中一个。但应用层感知不到冲突,以为操作成功了。
我测试的时候模拟过这个场景。两个连接同时更新同一条记录,一个成功一个报错。如果应用没处理这个错误,数据就丢了。
除非你的业务天然适合多写,要不还是老实选单主。
InnoDB Cluster:官方的"全家桶"
三件套怎么配合
InnoDB Cluster是MySQL官方的高可用方案,由三个组件组成:
Group Replication负责数据复制和一致性。就是上面讲的MGR,这里用的是单主模式。
MySQL Shell是管理工具。集群的创建、成员管理、状态检查都通过Shell完成。
MySQL Router是透明代理。应用连Router不连MySQL。主库切换时,Router自动把请求路由到新主。应用端完全不用改代码。
这三个组件分工很清楚。Shell管运维,MGR管复制,Router管流量。主库挂了,MGR选新主,Router感知后自动切路由。应用根本不知道主库换过。
部署体验
InnoDB Cluster的部署比纯MGR省事多了。
// MySQL Shell中创建集群
dba.configureInstance('root@192.168.1.101:3306')
var cluster = dba.createCluster('myCluster')
// 添加其他节点
cluster.addInstance('root@192.168.1.102:3306')
cluster.addInstance('root@192.168.1.103:3306')
// 查看集群状态
cluster.status()
Shell帮你处理了MGR那堆繁琐的配置。不需要手动写my.cnf,不需要手动引导节点,两行命令搞定。
Router的配置也是自动生成的:
# 启动Router,自动读取集群元数据
mysqlrouter --bootstrap root@192.168.1.101:3306 --directory /etc/mysqlrouter
systemctl start mysqlrouter
Router启动后会在6446端口监听读写请求,6447端口监听只读请求。应用连6446就行,主库怎么切都不用改连接。
我第一次部署InnoDB Cluster,从零到三节点集群跑起来,一个半小时。MGR纯手工部署花了三个小时。
Router的心跳机制
Router通过定期查询集群元数据,来感知主库变化。默认心跳间隔是1秒。主库切换后,Router最多1秒就能切换路由。
但切换不是完全无损的。正在执行的请求会失败,应用层要自己做重试。
# Spring Boot配置示例
spring.datasource.hikari.connection-timeout: 3000
spring.datasource.hikari.maximum-pool-size: 20
我测过,主库切换期间,HikariCP默认重试3次,每次间隔1秒。配合Router的1秒感知,业务恢复总耗时大概5-6秒。
故障切换与运维:三种方案全面对比
故障切换耗时
我在三节点环境下模拟主库宕机,每种方案测了十次取平均值:
| 方案 | 平均RTO | 最长RTO | RPO |
|---|---|---|---|
| MHA | 12秒 | 18秒 | 最多丢1-2秒 |
| MGR单主 | 5秒 | 8秒 | 零丢失 |
| InnoDB Cluster | 6秒 | 9秒 | 零丢失 |
MHA慢在三个步骤串行执行。MGR和InnoDB Cluster快是因为基于共识协议,选主是毫秒级的。
MHA的数据丢失量取决于从库的复制延迟。从库越落后丢得越多。MGR和InnoDB Cluster因为多数派提交,不存在这个问题。
部署与运维
| 维度 | MHA | MGR | InnoDB Cluster |
|---|---|---|---|
| 部署耗时 | 约2小时 | 约3小时 | 约1.5小时 |
| 配置项 | 约20个 | 约35个 | 约15个 |
| 运维工具 | 命令行 | SQL命令 | MySQL Shell |
| 自动切换 | 需要VIP脚本 | 内置 | Router自动路由 |
| Manager单点 | 是 | 否 | 否 |
| MySQL版本 | 5.5及以上 | 5.7.17及以上 | 8.0及以上(推荐) |
MHA最大的问题是Manager单点。Manager挂了,集群还能跑,但没法自动切换。要么加个Keepalived做Manager高可用,要么接受这个风险。
MGR的运维工具就是SQL命令,没有专门的管理平台。状态检查要手动敲performance_schema里的表。不够直观。
InnoDB Shell的cluster.status()输出很清晰,一眼能看到每个节点的状态、延迟、角色。
选型决策框架
光看技术指标没用,得结合自己的业务场景和团队能力来选。
场景一:MySQL 8.0以上,中小团队
直接上InnoDB Cluster。部署简单,官方维护,Router对应用透明。
这是我现在推荐最多的方案。省心,出了问题社区资源也多。
场景二:还在5.7,短期升不了
用MHA。MGR虽然5.7就有了,但早期版本bug多,生产环境别冒险。5.7上MGR的流控、认证机制都有过hotfix。
场景三:数据一致性要求极高
MGR单主模式。Paxos协议保证零丢失,适合金融、支付场景。
但要注意写入延迟。如果业务对延迟敏感(比如要求写入p99<5ms),要先压测确认MGR的延迟在可接受范围。
场景四:不想折腾
云数据库的高可用版本。RDS自带主从切换,不需要自己操心架构。省下来的时间精力比方案本身值钱。
场景五:需要多写能力
MGR多主模式。但说实话,多主的冲突处理很复杂。我见过不少团队上了多主又退回单主的。除非业务天然适合多写,否则慎选。
避坑清单
MHA的SSH检测是单点依赖。 网络一抖就可能误判主库宕机。我在测试环境遇到过,主库好好的却触发了切换。后来加上TCP端口检测做双重验证才解决。网络环境不够稳定的话,一定要加secondary check。
MGR上生产之前,建议先压测摸清楚流控触发的QPS。我在压测时QPS到8000,流控就启动了,响应时间从2ms飙到200ms。调整那个流控阈值参数,同时开启从节点并行复制,基本能缓解。
InnoDB Cluster的Router不是完全无损切换。 切换期间正在执行的请求会失败。应用必须做好重试,否则用户会看到报错。建议连接池配好重试策略。
还有一点容易忽略。group_replication_bootstrap_group只在第一次引导时开启,之后必须关掉。忘了关的话,每次重启都会创建新组,其他节点全掉线。建议写进运维手册,或者用InnoDB Shell自动管理。
不管哪种方案,从库必须开super_read_only。我见过不止一次,开发误连从库写数据。排查了好久才发现,这个要写进标准搭建流程里。
回到开头的问题。三种方案各有所长:MHA胜在成熟和兼容老版本,MGR胜在数据零丢失,InnoDB Cluster胜在易用和生态。但没有哪个方案能解决所有问题,选型的关键是匹配你的业务和团队。
你们生产环境用的哪种高可用方案?踩过什么坑?评论区聊聊。
我是数据库小学妹,咱们下篇见 👋
- 点赞
- 收藏
- 关注作者
评论(0)