MySQL高可用方案-从单点到集群的实战之路
MySQL 高可用方案从单机裸奔到稳如老狗的进化之路最近有个朋友问我“我们公司的 MySQL 就一台服务器昨天挂了一小时老板脸都绿了有啥办法能让它高可用”我拍了拍他的肩膀说“兄弟你这可是在’单机裸奔’啊…”一、先聊聊为啥单机 MySQL 这么让人心慌假设你正在做一个电商项目数据库就一台 MySQL。某天半夜这台服务器的硬盘挂了——用户下不了单、看不了商品、登录不了账号。你爬起来修修完发现数据还丢了半小时。这就是单点故障的恐怖。说白了MySQL 高可用要解决的就三个问题机器挂了怎么办—— 快速切换到备用节点数据丢了怎么办—— 实时同步保证数据不丢切换过程用户有感吗—— 尽量做到无感知咱们今天就聊聊MySQL 高可用到底有哪些方案以及怎么一步步搭起来。二、方案选型不是越贵越好是越适合越好我一开始也以为高可用就是上集群、上中间件越复杂越牛逼。后来踩了坑才发现——选型要看业务阶段别杀鸡用牛刀。常见的几种方案对比方案原理优点缺点适合场景主从复制 手动切换一主多从binlog 同步简单、成本低切换慢、可能丢数据小公司、读多写少、能接受分钟级中断MHAMaster High Availability主从 自动故障检测和切换自动切换、成熟稳定需要 SSH、可能会有几秒丢数据中型业务、需要自动故障恢复MGRMySQL Group Replication基于 Paxos 的组复制数据强一致、自动选主性能开销大、版本要求高金融级、对一致性要求极高InnoDB ClusterMGR MySQL Shell Router官方出品、集成度高学习曲线陡想走官方路线、团队技术强ProxySQL / MyCAT 中间件在应用和 DB 之间加代理层读写分离透明、切换无感知引入新组件、增加复杂度大型业务、读写分离需求强烈我那个朋友听完说“我们就几台机器MGR 是不是太重了”我说“对头先从主从复制MHA 走起够你用了。”三、实战手把手搭一个主从 MHA 架构咱们就以最常见的一主两从 MHA方案为例step by step 走一遍。第一步先把主从复制跑起来这是高可用的地基地基不牢后面全白搭。# my.cnf 主库配置 [mysqld] server-id 1 # 关键每台机器的 ID 必须唯一 log-bin mysql-bin # 开启 binlog binlog-format ROW # 推荐 ROW 格式数据一致性更好 expire_logs_days 7 # binlog 保留 7 天别撑爆磁盘 # GTID 强烈建议开后面切换省心多了 gtid_mode ON enforce_gtid_consistency ON# my.cnf 从库配置两台从库类似server-id 不同 [mysqld] server-id 2 # 另一台从库改成 3 log-bin mysql-bin # 从库也要开 binlog为后面可能晋升主库做准备 relay-log mysql-relay-bin gtid_mode ON enforce_gtid_consistency ON read_only ON # 从库只读防止误写入-- 主库上创建复制账号CREATEUSERrepl%IDENTIFIEDBY你的强密码;GRANTREPLICATIONSLAVEON*.*TOrepl%;-- 从库上执行指向主库CHANGE MASTERTOMASTER_HOST主库IP,MASTER_USERrepl,MASTER_PASSWORD你的强密码,MASTER_AUTO_POSITION1;-- GTID 方式不用记 binlog 文件名和位置STARTSLAVE;-- 检查从库状态SHOWSLAVESTATUS\G-- 重点看这两个Slave_IO_Running: Yes 和 Slave_SQL_Running: Yes关键点server-id绝对不能重复否则复制会乱套GTID 模式强烈推荐后面主从切换不用找 binlog 位置省心从库加read_only防止应用误写到从库第二步验证主从同步是否正常-- 主库上建个测试库CREATEDATABASEha_test;USEha_test;CREATETABLEt1(idINTPRIMARYKEY,nameVARCHAR(20));INSERTINTOt1VALUES(1,主从同步测试);-- 到从库上查USEha_test;SELECT*FROMt1;-- 如果能查到数据说明同步 OK第三步部署 MHA实现自动故障切换MHA 由两部分组成MHA Manager监控主库状态挂了自动切MHA Node跑在每个 MySQL 节点上做日志转发和恢复# 所有节点安装 MHA Node # 依赖perl-DBD-MySQLyuminstall-yperl-DBD-MySQLrpm-ivhmha4mysql-node-0.58-0.el7.centos.noarch.rpm# Manager 节点额外安装 MHA Manager yuminstall-yperl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManagerrpm-ivhmha4mysql-manager-0.58-0.el7.centos.noarch.rpm# MHA 配置文件 /etc/mha/app1.cnf [server default] usermha_admin # 监控账号 passwordxxx repl_userrepl # 复制账号 repl_passwordxxx ssh_userroot # 需要 SSH 免密登录 manager_workdir/var/log/mha/app1 remote_workdir/var/log/mha/app1 # 关键切换脚本可以在这里加 VIP 漂移或 DNS 切换 master_ip_failover_script/usr/local/bin/master_ip_failover [server1] hostname192.168.1.10 # 主库 port3306 candidate_master1 # 优先晋升为主库 [server2] hostname192.168.1.11 # 从库1 port3306 candidate_master1 [server3] hostname192.168.1.12 # 从库2延迟从库不参选主库 port3306 no_master1# 检查 SSH 连通性MHA 依赖 SSHmasterha_check_ssh--conf/etc/mha/app1.cnf# 检查复制状态masterha_check_repl--conf/etc/mha/app1.cnf# 启动 MHA 监控后台运行nohupmasterha_manager--conf/etc/mha/app1.cnf/var/log/mha/app1/manager.log21这里有个坑我踩过MHA 默认只做故障切换不会自动把原主库加回来。原主库修好后需要手动重新配置为从库。如果你希望完全自动化得自己写脚本或者上 Orchestrator。第四步故障切换测试别等真挂了才验证# 模拟主库宕机直接停掉主库 MySQLsystemctl stop mysqld# 观察 MHA 日志tail-f/var/log/mha/app1/manager.log# 正常会看到# - 检测到主库失联# - 选择最新的从库晋升# - 其他从库重新指向新主库# - 发送告警如果你配置了的话-- 到晋升的从库上检查SHOWMASTERSTATUS;-- 确认它现在已经是主库了SHOWSLAVE HOSTS;-- 看其他从库是否连上来了四、进阶读写分离怎么做高可用搞定了但还有个问题主库既读又写压力山大。这时候需要读写分离。方案对比方案说明应用层控制代码里写两个数据源手动指定读从库、写主库中间件ProxySQL在应用和 DB 之间加一层代理自动分发推荐用 ProxySQL对应用零侵入。-- ProxySQL 配置示例精简版-- 1. 添加后端 MySQL 节点INSERTINTOmysql_servers(hostgroup_id,hostname,port)VALUES(1,192.168.1.10,3306),-- hostgroup 1 写组主库(2,192.168.1.11,3306),-- hostgroup 2 读组从库(2,192.168.1.12,3306);-- 2. 配置读写分离规则INSERTINTOmysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply)VALUES(1,1,^SELECT.*FOR UPDATE,1,1),-- 带锁的读走主库(2,1,^SELECT,2,1);-- 普通 SELECT走从库-- 3. 加载配置LOADMYSQL SERVERSTORUNTIME;LOADMYSQL QUERY RULESTORUNTIME;SAVEMYSQL SERVERSTODISK;SAVEMYSQL QUERY RULESTODISK;关键点SELECT ... FOR UPDATE必须走主库否则数据不一致从库有延迟对实时性要求高的查询比如刚下单就查订单也要走主库ProxySQL 可以配置延迟检测某个从库延迟太大就自动踢出读组五、再进一步如果 MHA 也挂了怎么办这是个好问题。MHA Manager 是单点它挂了谁来切换解决方案Manager 本身也要高可用用 Keepalived 做 Manager 的双机热备或者直接把 Manager 部署在从库上从库挂了也不影响主库更现代的方案Orchestrator基于 Raft 共识算法可以多节点部署支持 Web UI 可视化拓扑支持自动恢复和手动切换GitHub 开源社区活跃# Orchestrator 简化配置思路# 1. 部署多个 Orchestrator 节点组成 Raft 集群# 2. 配置 discovery自动发现 MySQL 拓扑# 3. 配置 hooks切换时调用脚本做 VIP 漂移或 DNS 切换# 4. 配置告警切换时发钉钉/飞书/邮件六、总结一张图看懂选型业务规模小单库几GBQPS 1000 ↓ 主从复制 手动切换先有个备份心里不慌 ↓ 业务增长QPS 几千不能容忍分钟级中断 ↓ 主从 MHA 自动切换几分钟内自动恢复 ↓ 读写压力大读多写少主库扛不住 ↓ 主从 MHA ProxySQL 读写分离读流量分散 ↓ 金融级要求数据不能丢一致性要求极高 ↓ MGR / InnoDB Cluster强一致自动选主七、最后说几句MySQL 高可用没有银弹适合业务的才是最好的。小公司别一上来就 MGR运维成本扛不住大公司也别抱着 MHA 不放该升级就升级不管用啥方案一定要做故障演练别等真挂了才发现配置错了我那个朋友听完回去搭了主从MHA还写了切换演练手册。上周他跟我说“昨晚主库挂了MHA 30 秒切过去用户基本没感知老板终于不脸绿了。”你在实际项目中是怎么做 MySQL 高可用的用过 MGR 或者 Orchestrator 吗欢迎在评论区交流参考资料MySQL 官方文档 - ReplicationMHA 官方 WikiProxySQL 官方文档《MySQL 运维内参》