MySQL集群架构这个词,从我第一次接触数据库到现在,几乎贯穿了整个职业经历。单机MySQL能扛到一定程度,但流量一大、对可用性要求一高、数据量一涨,单点就开始成为瓶颈,光靠硬件堆上去总有个头。真正在生产环境把集群落地,远不是几台机器做主从复制那么简单,它涉及复制协议、高可用切换、读写流量调度、数据一致性、故障恢复、监控告警,每一层都有坑。这篇文章把我这些年从主从复制,到读写分离,再到InnoDB Cluster和分库分表改造的经验过一遍,重点讲选型逻辑、实操细节和踩坑过程,适合正要给线上库做集群化改造的同学,也适合刚接手集群但不想交太多学费的人。
1. 先想清楚:集群要解决的到底是哪个问题
1.1 集群的三个核心诉求
很多人一上来就嚷着“我要做MySQL集群”,但细问一句想解决什么问题,往往答不清楚。集群不是一个单一方案,它能解决的问题大致归成三类:可用性、容量、性能。
可用性问题是说,单台机器挂了一次,业务就停摆几十分钟,甚至要等运维去拉机。这种场景的核心诉求是“主库挂了系统还能继续写”,一般靠主从复制加自动切换解决。容量问题则是数据量涨得太快,磁盘快满了,单表几个T,备份和恢复慢到让人崩溃。性能问题最常见的是读写并发上去了,主库一条慢SQL就能拖垮整个实例,这时候需要把读流量分流出去,读多写少的场景尤其明显。
选型之前先把这三件事分开。我见过不少人把读写分离当成高可用来做,结果主库一挂,应用还是整个不能用,因为中间根本没有自动切换。也见过业务数据量还不到1T就开始分库分表,把查询复杂度抬上去了好几年,最后全是分布式事务和跨库join的债。更常见的是一套机房刚扩容完没两年,又因为容量瓶颈推翻重来。所以下面这张表是我每次做方案评审时都会先拿出来对一遍的。
| 核心诉求 | 典型瓶颈 | 建议方向 | 改造复杂度 |
|---|---|---|---|
| 可用性 | 单点故障、机房断电、网络分区 | 主从复制 + 自动切换 | 中 |
| 容量 | 磁盘空间、单表过大、备份恢复慢 | 分库分表 / 分区 + 归档 | 高 |
| 性能 | 读写并发高、慢查询拖垮主库 | 读写分离 + 缓存层中间件 | 中 |
1.2 从单机到集群的演进路径
大多数业务不是一开始就需要完整集群的,而是一步步走过来的。我在实际项目中见到比较健康的演进路径是这样:先是单机MySQL扛住早期流量,等延迟劣化到不可接受,先把备库加进来做只读,也就是主从复制。接着发现每次主库切换靠人工,半个小时都恢复不了,再上自动切换方案。再往后,某个业务线数据量成为整个集群的瓶颈,才考虑分库分表。
这个演进过程中每一步都有理由,也都有代价。加备库意味着要处理复制延迟;做自动切换意味着要设计好仲裁规则和脑裂防护;做分库分表意味着要重写一部分SQL、收编业务边界。我的建议是:不要让架构走在业务前面,也不要让业务把架构逼到墙角才动。关键是出现明确信号时,比如监控里频繁出慢查询、主从延迟持续升高、备份时间越来越逼近业务高峰期,就要认真做规划,而不是等到故障发生才处理。
2. 主从复制:所有集群方案的地基
2.1 复制到底在复制什么
主从复制是MySQL几乎所有高可用方案的基础。它的原理一句话就能讲明白:主库把每一次写操作记录到binlog里,从库拉取binlog并回放,使从库和主库的数据保持一致。
打个比方,主库像个会计,所有账目都记在账本(binlog)里;从库拿着这本账,在上面把每一笔账目重算一遍。GTID(全局事务标识符)就是账本的流水号,每个事务都有唯一编号。没有GTID的时候,从库靠“文件名+文件偏移量”定位复制位置,一旦文件被清理或者偏移出错,对账麻烦而且容易中断;有了GTID之后,从库只需要说“我执行到某个编号了”,主库就知道接下来该传哪一段日志,复制链路的管理和切换方便了一大截。
复制里还有一个容易忽略的点:binlog_format。我这些年只推荐ROW格式(有特殊场景另说)。STATEMENT格式记录的是SQL语句本身,虽然日志体积小,但从库执行函数、存储过程、UUID这些操作时,计算结果可能和主库不一致,因为受时间、环境、触发时机影响。ROW格式直接记录每一行数据的前后镜像,物理上更精确,恢复和校验都更可靠。代价是日志量会变大,尤其大事务update几十万行时binlog膨胀明显,这个要用下面的半同步机制和监控兜住。
2.2 生产环境搭建主从的标准步骤
搭建一套主从,光会连接是不行的。以MySQL 8.0为例,我给大家一套比较标准的流程,过程中记录下容易踩到的地方。
第一步是主库参数。在my.cnf里至少确保这些配置:
[mysqld] server-id = 1 log-bin = mysql-bin binlog_format = ROW gtid_mode = ON enforce_gtid_consistency = ON binlog_expire_logs_seconds = 2592000server-id每台实例必须不同,这是硬性要求。gtid_mode=ON和enforce_gtid_consistency=ON是为了启用GTID复制。binlog保留时间根据磁盘容量来定,我一般保留30天,给从库重建留足时间。
第二步创建复制账号,并限定来源IP,不要用root来做复制:
CREATE USER 'repl'@'10.0.%.%' IDENTIFIED BY 'password'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'10.0.%.%';第三步从库配置。同样设置独立的server-id,开启relay log。有一个容易忽略的地方:如果从库未来可能作为主库升级,binlog也一样要开,否则后面做级联复制或切换时会发现晋升的库没有日志。
第四步是全量初始化。如果从库是全新空库,直接CHANGE MASTER即可;如果主库已经有大量数据,不能先手动导入一份dump再开始复制,因为dump开始时间和复制开始时间之间可能少了日志。正确做法是建议用备份工具(比如开源的物理备份工具)做全量备份,从备份文件恢复出从库,这样备份点对应的binlog位置是内置的,恢复完成就可以无缝接续复制。没有完整备份条件的话,逻辑dump时需要额外记录MASTER_LOG_FILE和MASTER_LOG_POS,操作上更容易出错。
第五步在GTID模式下配置复制:
CHANGE MASTER TO MASTER_HOST = '主库IP', MASTER_PORT = 3306, MASTER_USER = 'repl', MASTER_PASSWORD = 'password', MASTER_AUTO_POSITION = 1; START SLAVE;最后检查状态:
SHOW SLAVE STATUS\G重点关注Slave_IO_Running和Slave_SQL_Running是否都为Yes,Seconds_Behind_Master是否为0(刚搭建时有短暂延迟是正常的)。到这里主从就从零到一跑起来了。
2.3 半同步复制和数据安全
异步复制最大的隐患是什么?主库提交事务后,binlog还没传到从库,主库突然断电了。这时从库缺少这笔事务,而主库可能又起不来,一旦切换,数据就丢了。金融类和强一致性业务无法接受这种丢失。
半同步复制就是解决这个问题的。主库在提交事务后,要等至少一个从库收到binlog并返回确认,才向客户端返回提交成功。如果等不到确认,主库会自动超时退回异步模式,继续提供服务,避免业务长时间阻塞。这相当于从“记账后不检查”升级成“账本至少一个分馆收讫了才盖章”。
启用方式是加载插件,MySQL 8.0里源端插件叫rpl_semi_sync_source,5.7时代叫semisync_master.so,从库对应地加载rpl_semi_sync_replica:
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so'; INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so'; SET GLOBAL rpl_semi_sync_source_enabled = 1; SET GLOBAL rpl_semi_sync_replica_enabled = 1;我在生产环境里会建议把半同步做成强制的,至少保证多数从库收到日志。同时必须监控超时回退的次数,如果经常回退说明从库确认太慢,网络或者从库装载能力有问题,要去查根因,不能靠侥幸。
2.4 主从延迟为什么不能被忽略
主从延迟的元凶主要有几类:大事务(一条UPDATE改了百万行)、DDL操作、从库单线程回放跟不上主库并发写入、从库上又叠了大量查询拖慢了执行。MySQL 8.0默认支持并行复制(MTS),能明显缓解,但日志回放的速度不等于主库执行速度,延迟依然可能存在。
Seconds_Behind_Master这个指标很多人只看它是否归零,其实它并不精确,本质是根据主从时间戳估算出来的。更可靠的方式是看GTID差距,用SHOW SLAVE STATUS\G里的Retrieved_Gtid_Set和Executed_Gtid_Set做差集,差集越大说明堆积的事务越多。
延迟的影响不只是读旧数据。主库故障切换时,如果被提升的从库还有延迟,那部分未回放事务就丢了。所以做高可用切换时,延迟检查永远是第一道闸门,延迟超过阈值的节点不能参与提升,否则切换完数据对不上。
3. 高可用方案:别在切换时掉链子
3.1 高可用不只是“自动切换”
很多人理解的MySQL高可用就是主库挂了、脚本自动把从库顶上,这其实只解决了一半问题。真正的高可用要同时回答三个问题:故障怎么发现,发现后怎么避免脑裂,切换后数据丢不丢。自动切换做得好,看起来只是几秒钟的事;做得不好,反而成了第二事故源头。
最典型的场景就是网络分区。主库和从库之间的网络抖动,节点间互相失去心跳,两个节点上都以为对方挂了,于是从库尝试自己成为主库,结果网络恢复后出现两个主库同时可写的状态,写入冲突就来了。这叫做脑裂。解决它不能靠“大家别抢”的自觉,必须靠仲裁机制。组复制(Group Replication)用分布式共识去选主,天然有仲裁概念;传统的MHA脚本做切换,则需要配合VIP、物理端口隔离、fencing脚本来确保旧主不再对外服务。
我在真实生产环境里学到的教训是:没有fencing的自动切换不如不切换。后来的方案里,不管用哪种高可用组件,都要预留“把旧主彻底踢出集群”的手段,比如断掉网卡、拔掉光纤、回收VIP,宁可多一个不可用窗口,也不要出现两主并写。
3.2 老牌方案MHA和Orchestrator
MHA是很多年前用得非常多的一套MySQL高可用方案。它的思路是有一个Manager节点监控主库,主库故障后自动从备选从库中选出新主,并通过SSH到各节点补拉relay log,尽量把日志补完整再切换。MHA在当年算是很成熟的存在,切换速度通常在30秒内。但它需要额外的Manager节点,还要配置SSH免密,拓扑管理和发现能力也比较弱;更重要的是,这个项目本身已经停止维护了。新项目我不会再选它,存量项目用着没问题的可以继续,但要有替代预案。
Orchestrator是另一个开源方向,它用Go写的,自带Web界面和HTTP API。它最大的优势在于能够自动发现并展示整条复制拓扑,比如主库下面挂了几个从库、从库之间是级联还是并列,一目了然。切换时它也能通过API触发,方便对接自动化发布平台。和MHA相比,Orchestrator更活,更适合长期维护和二次开发。不过它不会像人一样处理所有中间态,切换前还是要自己做好延迟检查、数据补齐策略,不能完全当甩手掌柜。
3.3 官方现代方案:InnoDB Cluster
如果你问我新部署的环境用什么,我推荐优先看官方方案:InnoDB Cluster。它由三部分组成:MySQL Group Replication负责节点间的数据复制和自动选主,MySQL Shell负责创建和管理集群,MySQL Router作为应用接入的中间层,自动做读写流量的路由。
MGR(组复制)底层用了类似Paxos的分布式共识协议,节点间要通信协商,多数节点同意后才能提交事务并选主。这样设计出来的好处是强一致性体验比普通主从好,也不容易出现“两个主”的情况,因为协议层面就投票选出了唯一主。搭建过程用MySQL Shell命令几步就能完成:
dba.configureInstance('root@主库IP:3306'); dba.createCluster('myCluster'); cluster.addInstance('root@从库1IP:3306'); cluster.addInstance('root@从库2IP:3306');之后用MySQL Shell配置Router:
mysqlrouter --bootstrap root@主库IP:3306 --directory /opt/mysql-routerRouter会自动识别集群里谁在写、谁在读,写是有方向性的从Router发出的。它会把写请求路由给当前主库,把读请求分发到别的成员,这样应用端不需要感知具体的库地址变化。
InnoDB Cluster也有坑。一个是网络要求,节点间通信频繁,网络质量差会导致频繁判定成员离线,所以我通常在隔离的专网里部署。另一个是节点数量,建议最少三个成员,两节点时一旦一个节点挂掉,剩下的一个节点无法形成多数派,写请求就会被停掉。如果你只有两个节点,可以用MySQL Router的仲裁功能或者额外加仲裁节点。还有个小坑,创建集群前要把已有的复制拓扑停掉改干净,不要拿着普通主从里的两台机器直接去configure,否则会有复制配置冲突。
3.4 我踩过的切换事故
分享一个真实的踩坑经历。某个环境我用MHA做切换,因为网络抖动,Manager判断主库失联,自动把一台从库提成了新主。可问题是,原主库并没有真的死掉,等网络恢复后,旧主和新的主库同时对外提供写服务,短时间内应用层就出现大量主键冲突。这不是MHA的问题,是我当时没有在切换流程里把旧主彻底隔离。后来我们的处理是:切换后立刻用脚本登到旧主,断开网卡、清掉VIP,再交给DBA手工处理。这件事给我的教训很深,做高可用,不光要会“选中新主”,还要会“杀掉旧主”,两件事必须作为一个整体来设计。
4. 读写分离与接入层治理
4.1 读写分离的正确姿势
读写分离本质上是把读的压力分散到从库,让主库专心服务写请求。什么时候应该做?我比较实际的判断标准是:读流量占整体请求的比例明显高于写流量,比如超过70%-80%,并且主库的QPS已经接近物理上限;或者报表查询、慢查询经常把主库CPU打满,业务写入跟着遭殃。
实现读写分离的方式有很多。最原始的是应用层自己判断,读走从库地址、写走主库地址。这种方式对应用侵入强,等切换主从时还得改配置,维护成本高。更推荐的是在接入层插一个透明中间件,比如MySQL官方Router、ProxySQL、ShardingSphere-Proxy。应用只需要连接中间件地址,中间件负责把读写SQL分发到不同的后端。
从中间件选型上看,Router适合和InnoDB Cluster配套使用,因为它天然感知集群内节点的角色变化。ProxySQL适合已经有普通主从架构、想更灵活控制路由规则的环境,比如你想对某些用户走读写分离、对某些查询强制走主库,它的规则引擎很强。
4.2 用ProxySQL落地读写分离
ProxySQL部署起来不复杂,核心就几张表。先说数据定义:mysql_replication_hostgroups表定义读写组编号,比如写组10、读组20。然后把实例和用户对应上去,最后加路由规则。
INSERT INTO mysql_replication_hostgroups (writer_hostgroup, reader_hostgroup, comment) VALUES (10, 20, '主从读写组'); INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (10, '主库IP', 3306); INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '从库IP', 3306); INSERT INTO mysql_users (username, password, default_hostgroup, transaction_persistent) VALUES ('appuser', 'password', 10, 1); INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply) VALUES (1, 1, '^SELECT .*', 20, 1);然后加载到运行时:
LOAD MYSQL SERVERS TO RUNTIME; SAVE MYSQL SERVERS TO DISK; LOAD MYSQL USERS TO RUNTIME; SAVE MYSQL USERS TO DISK; LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;这样一来,应用连上ProxySQL后,SSL安全和账号校验都走ProxySQL,SELECT自动到读组,其余写操作进写组。注意我给mysql_users里设置了transaction_persistent=1,它的含义是:一个事务内部的所有语句都绑定到同一个节点,不然你在写库里开了事务,中间去读从库,事务就“跑飞”了,数据也不一致。
这里有一个我强烈建议的属性:事务内读写应该固定在同一节点。很多刚上ProxySQL的团队忽略了这一点,出现“写完再读读不到”的问题,还以为是缓存,其实是路由把同一个事务里的读语句分到了别的从库上。
4.3 从库延迟的现实兜底
读写分离之后,延迟问题不会消失,只是摊到了下游。业务对实时性要求很高的读,比如刚下单就要看到订单详情,如果路由到从库,在延迟窗口内就会读到旧数据。这时候处理办法有三个层次。
第一层是规则层。在ProxySQL里为重要用户或特定表建强制走主库的规则,比如订单详情查询要求强一致读,规则里把目标hostgroup设成10。第二层是监控层。持续监控从库延迟,一旦某个从库Seconds_Behind_Master超过阈值,就在ProxySQL里把它摘除,避免它读到明显旧的数据。第三层是业务层。在需要“读到刚写入的数据”的操作里,前端先缓存一个标记,比如写入完成时记录时间戳,读请求小于这个时间戳就强制走主库。
这三个层次我建议至少要落地前两层,第三层看业务复杂度。很多团队连第二层都没有,线上从库延迟半小时了,读写分离还在往身上压流量,数据那是相当“滞后”。
5. 容量扩展:从集群到分库分表
5.1 不要轻易分库分表
如果说高可用的坑是“切了没隔离”,那容量扩展的坑就是“拆了没想清楚”。分库分表听着很高级,代价也很沉重。一旦分片,跨节点的join基本就废了,分布式事务成为常客,全局唯一ID、排序、分页都要另起炉灶。这个复杂度是呈指数级上升的。
所以我提倡第一原则:能不分就不分。单表数据量到了几千万条,不代表必须分表,先看索引是否合理、有无长期没被利用的大字段、是否可以做归档和分区。MySQL 8.0对分区表和InnoDB的性能调优比前代好不少,几张热表做分区,把历史数据移到低频分区,存储和查询压力能释放一大半。只有当单实例写入吞吐达到瓶颈、单库容量已经到几十TB、业务边界清晰确实需要垂直拆分时,才轮到分库分表。
5.2 分片策略怎么定
分片策略的选择,本质上是在“数据分布均匀”和“业务访问局部性”之间找平衡。
按用户维度分片是最常见的做法,比如按user_id取模,或者按用户ID范围路由。这样做的好处是同一个用户的数据基本落在同一分片,用户的所有查询都在本地完成,不需要跨节点。缺点是如果某个大用户的单条数据量特别大,这一片会比其他片都热,形成数据倾斜,需要考虑热用户二次拆分。
按时间维度分片适合日志、流水这种只关心最近数据的场景。比如按月建表,查历史时扫对应的月度分片,过期数据可以整表归档,运维成本低。缺点是写入热点集中在当前时间分片,只能解决容量和归档问题,解决不了单点写入性能瓶颈。
按业务维度垂直拆分适合不同业务线的库原本就耦合在一起的情况,比如把用户库、订单库、商品库拆到不同集群。这个方案依赖组织边界和研发分工,如果两个团队都想改同一张表,拆分计划基本做不下去,需要先梳理系统间的调用关系,把跨库的join干掉。
5.3 中间件选型与迁移实操
分库分表中间件主要分两类。一类是应用层嵌入型,比如ShardingSphere-JDBC,它在一个依赖里完成数据源路由、SQL解析、结果聚合,应用通过它连数据库,不额外增加网络代理。好处是性能损耗小、部署简单,坏处是每个接入的应用都要引依赖,版本升级要跟着应用走。另一类是代理型,比如MyCat、ShardingSphere-Proxy,它们独立部署在应用和数据库之间,应用不用改连接方式,但网络链路更长,代理节点本身也会成为新的瓶颈。
选型时主要看团队维护能力和现有架构。老系统不想动、兼容要求高,选代理型比较稳;新项目微服务集群、技术栈统一,选嵌入型更顺手。
迁移最怕的是闪断。常规流程是这样:先全量同步老库数据到新分片,全部追平后,通过binlog订阅机制把增量也追到一致,接着开启双写,新库开始同步写入。双写阶段重点做数据校验,比对主键和关键字段,确认没有差异后再把读流量切换过去,最后切写流量、下线老库。整个过程里最危险的永远是“还没完全一致就切写”,所以我强烈建议在校验工具上多花时间,不能只看行数一致,要用字段级抽样甚至全量哈希对比。
5.4 拆分后的几个附加问题
分库分表做完了,还有几件事必须同步处理掉,否则后面跑得很难受。
第一是全局唯一ID。原来的自增主键不能用了,因为每个分片的自增值会重复。我见过有团队用“时间戳+随机数”拼一个ID,虽然简单但碰撞风险和大并发下的唯一性保证堪忧。更合理的是用号段模式或雪花算法变体,ID里自带时间戳和机器编号,生成器本身要保证高可用,避免生成服务挂了导致全系统无法写库。
第二是跨分片查询。设计分片路由时,要逼着业务把大多数查询定位到单分片。怎么实现?查询条件必须带有分片键,比如按用户维度分片,所有查询都要带user_id。如果出现了大量按订单号查询的场景,就得另外建全局索引表或者反过来按订单号分片。分片键设计不收敛的话,后面会逼着团队做一堆反向索引,复杂度再度上升。
第三是全局字典表。比如商品分类、国家地区这类配置型数据,几乎每次查询都要用到,把它们放到每个分片里各保存一份,避免每次查都走跨库路由。这里需要一套同步机制,确保各分片里的字典表长期一致,我自己习惯用消息队列异步推送,配合离线校验兜底。
第四是分布式事务。能不做就不做,业务上能用最终一致性就不要追求强一致。比如一个流程里要同时更新用户库和订单库,可以先更新一个库,发消息给对方,对方消费后更新自身;如果对方更新失败,靠回调重试或者定时对账补偿。直接用分布式事务框架不是不可以,但会显著增加锁竞争和故障复杂度,很多业务场景用消息兜底更合适。
6. 集群常见故障与排查速查
6.1 复制中断的典型故障
数据库集群的故障类型虽然多,但复制链路出问题占到了相当大的比例。下面这张表是我在实际运维中总结的头部故障,基本可以拿来当速查卡。
| 错误类型 | 典型报错 | 常见原因 | 处理方式 |
|---|---|---|---|
| 主键冲突 | Error 1062 Duplicate entry | 手动改了从库数据、回放重复 | 核对两端数据差异,必要时重建从库 |
| binlog日志丢失 | Error 1236 Could not find first log file name | binlog被清理或从库IO位置过旧 | 只能全量重建从库,不能跳过日志继续复制 |
| 网络抖动 | 半同步超时回退 | 主从带宽、交换机问题 | 排查网络,适当调整超时参数 |
| 时钟不一致 | Seconds_Behind_Master虚高 | 没有时间同步服务 | 统一部署NTP时间同步 |
| 大事务导致延迟 | 无报错,但延迟一直涨 | 大量行更新的DML或DDL | 并行复制、错峰执行、控制单事务影响行数 |
我最想重点说的是1236这个错误。很多人刚遇到时想着能不能从某个binlog文件往后跳,跳过丢失的日志继续复制。我明确告诉你,不可以。GTID模式下主库日志都清了,你跳过去的那些位置根本对不上,后面再发生问题数据就真的完全不可信了。正确处理是停掉复制、全量重新初始化从库、再起来追平。看起来要花时间,但比起带着一个不知错在哪里的从库跑,重建才是最节省时间的方案。
6.2 延迟治理的实操
延迟治理没有银弹,通常是多管齐下。首先MySQL 8.0里确认并行复制开着,参数slave_parallel_workers配置为多线程,类型用LOGICAL_CLOCK。然后分析延迟来源。如果是大事务,要在源头上控制单个事务影响的行数,比如批量update拆成批次执行,每批一两千行,commit后再跑下一批。如果是DDL,尽量用在线DDL工具,避免锁表拖垮从库。如果从库还承担了大量查询,要限制从库的CPU和IO使用,config里可以设置innodb_io_capacity、max_connections,甚至把不重要的只读业务挪到其他专门的分析实例上。
监控层面建议两条腿走路:除了看Seconds_Behind_Master,还要看GTID差集变化。可以用一条SQL在主库上看Executed_Gtid_Set,在从库上看Retrieved和Executed,计算差集。差集持续增大说明堆积在加速,这时即使Seconds_Behind_Master显示为0也不能大意,因为大事务没提交完时时间戳可能显示不出来。
6.3 健康检查脚本的要点
最后给一套自己常用在监控里的检查项目,不打成一个完整脚本,但给大家列出核心SQL,组合起来就能用。
先看复制是否正常:
SHOW SLAVE STATUS\G -- 检查 Slave_IO_Running, Slave_SQL_Running, Retrieved_Gtid_Set, Executed_Gtid_Set看主库写了多少、从库能跟上多少,可以看每个实例的binlog大小:
SHOW MASTER STATUS; SHOW BINARY LOGS;看从库是否意外变成可写,这是一个很容易忽视的安全项。从库一般要设成只读模式,如果它被谁改成非只读,复制一旦回放冲突,后果很严重:
SHOW VARIABLES LIKE 'read_only';看半同步是否退化成了异步,这个指标我遇到过很多人没监控:
SHOW STATUS LIKE 'Rpl_semi_sync_source_status'; SHOW VARIABLES LIKE 'rpl_semi_sync_source_enabled';如果查询结果是OFF或者变成了0,说明半同步已经超时回退过了,需要去查原因。再补一个重要的:binlog过期时间,如果设置太短,从库出问题后根本来不及追,要结合自己的从库延迟稳定性去调整。
这些监控项不用多,能覆盖复制链路、延迟、半同步、只读状态,就足以让你在故障发生前发现80%的问题。
最后说一点个人体会。搞MySQL集群这件事,最难的从来不是搭起来,而是搭完之后长期维护不出事故。我踩过延迟、脑裂、复制中断这些坑以后,最大的心得是:任何自动化手段都只是辅助,最终可靠的是清晰的监控、可验证的数据一致性、和一套出问题时能快速执行的应急预案。你在做集群改造的时候,不妨先把上面这些场景模拟一遍,在测试环境里真真切切杀一次主库、断一次网络,观察你的系统会怎么反应。演练时出的问题,就是上线前最值钱的改进空间。这篇文章能帮你把路铺平一点,但真正的路,还是要自己走一遍。