最近后台收到不少朋友问 MySQL 高可用怎么做,尤其是那些业务刚起步、还没专门DBA的团队,一说“高可用”就想着上什么重量级方案,结果把自己折腾得够呛。我这些年帮别人搭过、也自己维护过不少 MySQL 集群,从最早的主从复制加 MHA,到后来用 Orchestrator,再到 MySQL 官方的 InnoDB Cluster,踩过的坑确实不少。这篇就把我实际用下来觉得最靠谱的几种方案、背后的原理,以及那些文档里不会写但关键时刻要命的细节,一次性梳理清楚。
读这篇文章之前,你需要对 MySQL 的基本操作有概念,知道 binlog 大概是什么。我会尽量把每个方案“为什么这么设计”讲明白,而不是只丢一堆配置命令。如果你正在为 MySQL 选型高可用方案,或者已经搭好主从但不知道后续怎么运维,这篇文章应该能帮你省下几个星期的摸索时间。
1. 高可用到底在解决什么问题
1.1 从一次半夜故障说起
先讲个真实经历。前几年我负责一个电商项目的数据库,当时架构是“一主一从”,主库扛读写,从库只做备份和偶尔的报表查询。自我感觉挺良好,直到有一回凌晨两点,主库所在物理机磁盘报警,紧接着整个实例直接宕机。我爬起来把 VIP 切到从库,再把从库提升为主库,整个过程花了十几分钟。对技术团队来说这个速度算是正常操作,但对线上业务来说,这十几分钟就是十几分钟的交易损失,老板早上看到数据报表的时候脸色很不好看。
那次之后我一直在想,所谓的“高可用”,本质上不是“不出故障”,而是“出故障之后业务感觉不到,或者只感觉到很短的中断”。MySQL 单机再稳,也扛不住硬件损坏、机房断电、误操作这些不可控因素。高可用方案要解决的核心问题就三个:数据尽量不丢、故障自动恢复、切换过程尽量快。
1.2 高可用的几个层级
很多人一上来就聊 MGR、PXC,其实高可用是分层级的,不同业务对可用性的要求完全不一样,方案选型必须从业务需求倒推。
第一层是“数据有备份”,这是最底线的要求。定时全备加 binlog 增量备份,服务器炸了至少能恢复到某个时间点。但问题在于恢复时间很长,一个 500G 的实例,物理备份恢复可能要一两个小时,业务根本等不起。这一层只能叫“备份”,谈不上高可用。
第二层是“主从自动切换”。至少一个主库一个从库,主库挂了之后从库自动顶上。这里的关键不仅是“切换”这个动作,还包括怎么让客户端知道连哪个库、切换时主从数据不一致怎么办。MHA、Orchestrator 这些工具干的就是这件事。
第三层是“多节点同时提供服务”。读写分离、负载均衡,多个节点组成一个集群,任何一个节点挂掉,流量自动打到其他节点。MySQL Group Replication、InnoDB Cluster、Percona XtraDB Cluster 都是这个思路。
大多数中小团队做到第二层已经能满足 99% 的场景,没必要为了追求第三层把架构搞得过于复杂。我见过不少团队一上来就上 PXC,结果节点之间网络抖动就得整个集群同步阻塞,反而把可用性搞得更差。
1.3 可用性指标怎么看
聊高可用,绕不开“几个 9”这个概念。99.9%(三个9)意味着一年允许宕机 8.76 小时,99.99%(四个9)是 52.56 分钟,而 99.999%(五个9)一年只能挂 5.26 分钟。
大多数互联网业务,做到三个 9 到四个 9 之间已经算不错了。就我经验来看,一主一从加自动切换,正常情况下能把可用性做到三个 9 到四个 9 之间;想要稳定达到四个 9 以上,光靠切换还不够,得考虑多机房部署、网络冗余、应用层重试机制这些因素。
还有个容易被忽略的指标叫 RTO(恢复时间目标)和 RPO(恢复点目标)。RTO 是“挂了之后多久能恢复服务”,RPO 是“最多丢多少数据”。一主一从半同步复制,RPO 可以做到接近零,RTO 看切换脚本效率,通常几十秒。如果业务能接受丢几秒数据,异步复制加自动切换就够了,性能和简单性都更好。这两个指标在方案选型时必须先定下来,不然配置参数的时候根本没有判断依据。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高可用基石:MySQL 主从复制
2.1 binlog 和复制链路是怎么工作的
不管哪种高可用方案,底层都离不开主从复制。要理解 MySQL 主从复制,重点抓住三个线程:主库上的 Binlog Dump 线程、从库上的 IO 线程和 SQL 线程。
主库上所有写操作都会记录到 binlog,Binlog Dump 线程负责把 binlog 发送给从库;从库的 IO 线程接收这些日志并写入本地的 relay log(中继日志);SQL 线程再读取 relay log,顺序执行里面的 SQL,把数据变更在从库上重放一遍。这个过程是异步的,从库的数据总是落后主库一点点,落后多少取决于网络延迟和从库自身的执行速度。
这里有个关键点:binlog 记录的格式。MySQL 有三种 binlog 格式,STATEMENT 记录 SQL 原文,ROW 记录每一行数据的变更前后值,MIXED 是混合模式。做主从复制,我强烈建议用 ROW 格式。STATEMENT 格式在碰到 UUID()、NOW() 这些非确定性函数时,从库重放出来的结果很可能和主库不一致;而 ROW 格式记录的是行变更结果,天然一致。
MySQL 8.0 默认就是 ROW,实际上很多人用 5.7 时也手动改成 ROW。唯一的代价是 binlog 体积会变大,尤其是有大批量 UPDATE 的时候,但这点磁盘成本相对于数据一致性来说完全值得。
2.2 主从复制的核心参数
既然要搭主从,几个核心参数得先搞清楚。先说主库上的 server_id,这个必须设置,而且每个节点的 server_id 不能重复,它是复制拓扑中区分节点的唯一标识。
再说 binlog 相关参数。log_bin 开启 binlog,binlog_format 设为 ROW。建议把 expire_logs_days(8.0 里是 binlog_expire_logs_seconds)设成一个合理的值,我一般设 7 到 14 天,太短了追不上数据,太长了占磁盘。
从库上有两个参数容易忽略。一个是 relay_log_purge,默认是开启的,就是说 relay log 执行完会自动删除。这个参数建议保持默认,除非你想做延迟复制或者调试,否则不必要的 relay log 会撑爆磁盘。另一个是 read_only,从库上建议打开,防止有人误连从库写数据,导致主从数据不一致。注意 read_only 对超级管理员不生效,如果要彻底一点,可以加上 super_read_only。
还有一个参数叫 gtid_mode,GTID 是全局事务标识符,每个事务都有唯一编号,主从复制时通过 GTID 自动定位同步位置,不用手动指定 binlog 文件名和偏移量。强烈建议开启 GTID,后面做切换、加从库都会方便很多。开启方式是:
ini复制gtid_mode = ON
enforce_gtid_consistency = ON
这两个参数必须同时开,而且要确保 binlog_format 是 ROW,否则启动会报错。
注意:GTID 一旦开启,不要随便关掉。集群里的节点都开了 GTID 之后再切回传统复制模式,很容易出复制中断,而且排查起来非常麻烦。我见过有人嫌 GTID "太新"不想用,坚持传统位点复制,结果每次加新从库都要手动去找 binlog 位置,费时费力还容易出错。
