
凌晨两点告警平台开始轰炸从库延迟从几十秒一路飙到上千秒随后主库所在机器又报出磁盘异常。我当时的反应就是切换。可真正执行切换时才发现备选从库的IO线程还在反复重连另一台从库的SQL线程倒是追平了但主库的binlog文件究竟传到哪一格没人敢拍胸脯保证。最后拖了快一个小时才切完业务受损不说复盘时最大的收获是MySQL 8.0这套主从复制体系——异步、半同步、GTID、MGR——如果只停留在“会配置”的层面遇到真正的故障场景心里是真的没底。这篇文章就把整个复制机制从底层链路到生产落地完整梳理一遍。我的目标很明确看完之后你能讲清楚三个线程各自在做什么半同步的ACK到底卡在哪个环节GTID为什么能替代文件位点MGR又是靠什么机制做到自动选主。文章会同时给出MySQL 8.0下的配置命令、验证方法和踩坑记录适合刚接手数据库运维的同学也适合后端开发想搞明白“为什么主库一挂就抓瞎”的人。1. 复制链路基本功binlog、relay log与三线程的工作模型理解MySQL复制绕不开三个基本组件和三条线程。很多人配置过主从但问到底层数据是怎么从主库流转到从库的说不清楚。这里我先把这条链路完整拆开。1.1 三线程的分工与设计的巧妙之处主从复制从主库写入开始。主库上所有数据变更都会按顺序写入二进制日志binlogbinlog是复制的数据源。从库侧则有两条线程配合完成任务IO线程和SQL线程。链路是这样的从库的IO线程向主库发起连接主库为该连接启动一个专用的binlog dump线程。dump线程负责从指定binlog文件的指定偏移量开始把后续所有binlog事件推送给从库。从库IO线程收到事件后写入本地文件relay log中继日志然后SQL线程读取relay log里的事件逐条在从库上回放最终完成数据变更。三条线程分开设计不是随意为之。IO线程只管网络接收和落盘SQL线程只管本地回放两者互不阻塞。主库到从库之间的网络慢那IO线程慢慢收SQL线程依然可以消化已经收到的部分从库本地执行遇到锁等待SQL线程卡住IO线程也依然可以把新的binlog事件先攒下来。这种解耦设计让复制对网络抖动和本地压力的容忍度高了很多。dump线程也值得多说一句。它跟普通的客户端连接不太一样主库会为它维护一个发送游标每产生新binlog事件就推送给从库不需要从库反复轮询。这部分逻辑在MySQL 8.0里有不少优化比如事件压缩、并行发送等但基本工作模型没有变.1.2 relay log的接力逻辑与关键检查命令relay log平时很多人不太关注但主从延迟和复制中断的问题排查基本都落在它身上。从库IO线程收到binlog事件后写入本地relay log文件默认是relay-bin.00000x由relay_log参数控制同时更新relay-log.info文件记录当前已接收的binlog文件名和位点。SQL线程每回放一个事务也会更新这个文件记录已执行的位点。所以排查问题时的核心视角是IORes? IO线程拉到哪了SQL线程执行到哪了两者之间的差就是“已经接收但尚未回放”的量。MySQL 8.0中语法已经从MASTER/SLAVE全面改为SOURCE/REPLICA检查状态的命令也变了SHOW REPLICA STATUS\G重点关注这几个字段Replica_IO_Running: Yes Replica_SQL_Running: Yes Read_Master_Log_Pos: 262145 Exec_Master_Log_Pos: 262001 Seconds_Behind_Master: 0 Master_Log_File: binlog.000009 Relay_Master_Log_File: binlog.000009字段含义很直白。IO线程正常连接主库并且拉取binlog时Replica_IO_Running为YesSQL线程正常回放时Replica_SQL_Running为Yes。Read_Master_Log_Pos表示IO线程从主库binlog读到哪个偏移量Exec_Master_Log_Pos表示SQL线程已回放到哪个偏移量。Seconds_Behind_Master是根据binlog事件时间戳推算的延迟秒数。注意Seconds_Behind_Master这个值它本质是估算值。如果从库本地回放卡住但IO线程还在接收新事件这个值会增长如果SQL线程正在执行一个大事务这个值可能虚高如果从库时钟与主库不同步这个值也不准。所以定位延迟时我会同时把两个位点差值拉出来看比单看秒数靠谱得多。IO线程还在持续拉取说明网络和主库没问题问题集中在SQL线程或从库本身IO线程停在某个位点不动通常是连接断开、主库binlog被清理或者账号权限异常。1.3 文件位点复制的内核局限传统复制模式下从库要知道自己该从主库哪个binlog文件哪个位点开始拉取这个信息在搭建时由管理员手工指定。在主从正常的日常运行中位点信息由relay-log.info自动维护问题不大。但一旦涉及故障切换或主从重建这套文件位点机制就非常别扭主库崩溃后你根本不知道对该从库来说“下一位点”是哪里从库如果多执行了几个本地事务位点就跟主库对不上binlog被PURGE清理后从库请求的位点已经不存在只能全量重建。这些痛点直接催生了GTID复制。在讲GTID之前我们先把最基础的异步复制完整走一遍因为后面所有高级方案的配置和坑都建立在这一步之上。2. 异步复制原理、部署全流程与两个致命短板异步复制是最主流的复制模式几乎所有的读写分离架构都以它为底座。大多数情况下它跑得挺好但必须先认清它有一个结构性的短板主库提交事务后立刻返回客户端成功根本不管从库收到没有。这个短板平时不显眼故障时就是数据丢失的根源。2.1 主从参数配置与复制账号MySQL 8.0安装完成后默认log_bin是开启的这比5.7省事。但要注意以下参数是否满足要求主库和从库的server_id必须不一样这是保证复制链路能区分来源的基础。主库的binlog_format建议明确设置为ROW8.0默认就是这个但如果是从前版本升级上来的老库务必要确认。ROW格式下记录的是每一行的实际变更主从一致性最可靠STATEMENT格式在网络延迟高、函数不确定的场景容易造成两边数据不一致。从库建议开启read_onlyON和super_read_onlyON避免应用或人工直接连从库写入导致数据漂移。8.0中log_slave_updates默认开启这个参数控制从库回放事务时是否把自己的变更写入自己的binlog在级联复制和MGR场景中必须开启。主库上创建复制专用账号CREATE USER repl% IDENTIFIED BY YourStrongPass2024; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO repl%;REPLICATION SLAVE权限让账号能拉取binlogREPLICATION CLIENT权限允许执行SHOW REPLICA STATUS等监控命令。我只给这两个权限不给其他多余权限降低账号泄露后的风险。2.2 从零搭建的完整步骤假设主库和从库都装好了MySQL 8.0数据是空的搭建步骤如下。第一步在主库执行一次全量备份。这一步不建议用纯mysqldump导出而不用--source-data参数因为手工记录位点容易出错mysqldump -uroot -p --single-transaction --source-data2 --routines --triggers --all-databases /backup/master_dump.sql--single-transaction通过一致性快照保证备份期间数据一致不会锁住业务表--source-data2会在dump文件中注释记录当时的binlog文件名和位点。如果是5.7参数名是--master-data28.0改成SOURCE命名体系后是--source-data。第二步把dump文件传到从库并导入mysql -uroot -p /backup/master_dump.sql导入完成后找到dump文件开头那段注释-- CHANGE MASTER TO MASTER_LOG_FILEbinlog.000009, MASTER_LOG_POS262144;8.0下这套旧语法仍然兼容但新环境建议用新语法配置CHANGE REPLICATION SOURCE TO SOURCE_HOST192.168.1.10, SOURCE_PORT3306, SOURCE_USERrepl, SOURCE_PASSWORDYourStrongPass2024, SOURCE_LOG_FILEbinlog.000009, SOURCE_LOG_POS262144;然后启动复制START REPLICA; SHOW REPLICA STATUS\G看到Replica_IO_Running和Replica_SQL_Running都是Yes主从就正常了。之后可以插一条测试数据验证从库同步也可以用SYSTEM命令在主库执行写入。2.3 搭建和运行中的常见坑这套流程看似简单但我在实际运维中踩过不少坑列几个高发的server_id重复。两节点用了相同ID从库IO线程会反复报错日志里能看到类似Slave I/O for channel 信息。排查时把两边的server_id都看一眼别不信生产环境真有配置脚本批量下发导致重复的。dump文件里带了旧库的复制配置。如果从库之前配置过复制会报错或配置冲突。导入前建议先执行RESET REPLICA ALL;清掉旧配置。主库binlog被purge了。从库位点对应的binlog文件已经被清理IO线程报错并不断重连。这种情况只能重建从库没有捷径。所以备份策略里必须保留足够久的binlog我在生产环境保留至少3天。运行期还有一个容易忽略的问题主库执行大事务比如几百万行的UPDATEROW格式下产生的binlog可能有好几个GB如果从库的max_allowed_packet或slave_net_timeout配置不合理SQL线程可能直接宕掉或报错中断。8.0默认配置大都能扛但经过严格安全基线加固的小内存机器要注意调整。2.4 异步复制在故障切换时的最大痛点异步复制的问题一句话概括主库提交事务后不回等从库的relay log可能落后主库binlog好几个事务。假设主库在1点0分0秒提交了事务T100业务已经收到成功响应但dump线程还没来得及把T100的binlog事件推送到从库主库就宕机。此时把从库提升为新主库业务必然丢失T100而且客户端已经收到成功返回。更麻烦的是切换时你根本不知道从库到底落后多少。Seconds_Behind_Master可能显示0但这是异步的“估算延迟”不是“已确认提交且已同步”的保证。这里就让很多人理解错了——半同步复制就是为了补这个窟窿才出现的。3. 半同步复制从after_commit到after_sync的ACK演进半同步复制的目标很直接主库提交事务前至少要等到一个从库确认已经收到binlog事件。这样在绝大多数故障场景下主库提交过的事务一定已经存在于某个从库的relay log里切换后不会丢。3.1 半同步要解决什么问题以及它的等待时序MySQL 5.5引入半同步时插件模式和等待逻辑还比较粗糙到了5.7默认等待点是AFTER_COMMITMySQL 8.0把等待点变成了AFTER_SYNC并作为默认值。这两个等待点的时序差异非常关键直接关系到丢数据窗口的大小。AFTER_COMMIT5.7默认的时序是主库写binlog事务在存储引擎层提交主库等待一个从库返回ACKACK回来后再给客户端返回成功。问题出在步骤2和步骤3之间。如果主库在提交事务之后、等待ACK的过程中宕机那么这个事务已经提交成功但binlog事件可能还没被任意从库收到。客户端已经拿到成功响应切换后事务却丢了。AFTER_SYNC8.0默认的时序则调整为主库写binlog主库等待至少一个从库ACK确认binlog事件已经进入从库relay log收到ACK后事务在存储引擎层提交提交后返回客户端成功。这个顺序下主库只有在从库确实拿到binlog事件之后才会提交事务、告知客户端成功。也就是说“主库已提交”和“从库已收到binlog”被强制绑定在一起之前那个丢数据窗口被堵上了。但注意AFTER_SYNC不是零代价。ACK等待期间事务尚未提交相关行锁一直被持有并发写入被串行化锁等待和死锁概率都会上升。对写并发很高的业务这个延迟差异会直接反应在RT曲线上。另一个细节是它带来的语义变化如果主库在等待ACK期间宕机事务在从库并没有提交客户端也没有收到成功响应从库接管后该事务会被回滚整体语义是“宁可失败不能假成功”这比丢数据好得多。3.2 8.0中的配置方式与状态验证8.0把半同步从插件变成了内置机制配置参数也全面改名为SOURCE/REPLICA体系。主库设置plugin_load_addsemisync_source.so rpl_semi_sync_source_enabledON rpl_semi_sync_source_timeout10000从库设置plugin_load_addsemisync_replica.so rpl_semi_sync_replica_enabledON参数中的rpl_semi_sync_source_timeout是等待ACK的超时时间单位毫秒默认1000010秒。超过这个时间没有收到ACK半同步自动降级为异步不阻塞业务写入。这个参数不要拍脑袋设很小否则一次网络抖动就直接降级也不要设太大否则从库故障时主库写入会被长时间阻塞。如果是从5.7迁移过来的环境旧参数rpl_semi_sync_master_enabled、rpl_semi_sync_master_timeout在8.0中仍然兼容可用但建议逐步切换到新命名。配置完成后验证半同步是否生效SHOW STATUS LIKE Rpl_semi_sync%;重点看这几个状态值Rpl_semi_sync_source_status: ON Rpl_semi_sync_source_clients: 1 Rpl_semi_sync_source_no_tx: 0 Rpl_semi_sync_source_yes_tx: 12Rpl_semi_sync_source_status为ON表示半同步当前处于启用状态。Rpl_semi_sync_source_clients为1表示当前有一个从库在ACK如果这个值是0说明虽然配置了半同步但没有任何从库连接等于没启用。Rpl_semi_sync_source_yes_tx是成功走完半同步流程的事务数no_tx是收到ACK超时降级为异步的事务数后者持续增长就要引起重视了。部署完成后再做一次故障演练确认停掉从库的IO线程观察主库写事务时是否在超时阈值内阻塞再观察半同步状态是否从ON变成OFF。3.3 半同步退化的瞬间与监控的黄金指标半同步最容易被忽视的是自动退化机制。从库故障、网络分区、从库IO线程被大事务拖慢都可能导致ACK迟迟不到。一旦超过rpl_semi_sync_source_timeout主库日志会打出类似Timeout on waiting for semi-sync ACK from replica随后自动关闭半同步全面退回异步。这里有个容易误解的点很多人的认知是“半同步就安全了”但半同步只在它本身生效时安全。退化后到重新恢复之间的窗口复制和异步一样有丢数据风险。我在生产环境里见过不止一次因为从库磁盘满了半同步静默退化主库继续写业务根本无感知直到切换时才发现主从之间有缺口。所以必须监控这几个指标缺一不可SHOW STATUS LIKE Rpl_semi_sync_source_status; SHOW STATUS LIKE Rpl_semi_sync_source_clients; SHOW STATUS LIKE Rpl_semi_sync_source_yes_tx; SHOW STATUS LIKE Rpl_semi_sync_source_no_tx;source_yes_tx和source_no_tx是累计值可以从两个值的历史曲线判断退化频率。如果no_tx在增长说明系统频繁从半同步退化为异步要排查从库节点或网络。source_clients长期小于期望从库数也要查从库状态。这套监控凡是上了半同步的生产环境都应该做。不知道读者里有多少人是配完半同步之后从来没看过这些状态数字的我建议今天就去查一下自己的库。3.4 半同步的边界不保证从库已回放半同步保证的是“binlog事件已经进入从库的relay log”但它完全不保证SQL线程已经把事务回放完成。换句话说半同步解决了“丢binlog”的问题但没解决“从库延迟”的问题。这个边界很多架构师会搞混。半同步打开后从库的Seconds_Behind_Master依然可能高达几百秒甚至几万秒主库写入却完全不受影响因为ACK只代表IO线程落盘成功不代表SQL线程追平。在做主从切换前仍然要确认候选从库的SQL线程已经回放到和主库一致的位点否则切过去之后会有一段数据空洞业务读不到刚写入的数据。4. GTID让复制不再依赖文件和位点GTID的全称是Global Transaction Identifier全局事务标识符。它给每个事务分配一个全局唯一的ID复制链路通过这个ID来识别事务进度不再依赖binlog文件名加偏移量。4.1 GTID的数据结构、生成和存储GTID的格式是UUID:事务序号例如3f3f2ea5-5a77-11ee-8b88-005056c00001:1-45前半部分是主库实例的server_uuid后半部分是单调递增的事务序号。读写事务在主库上生成GTID每个事务生成一个新的序号从库回放事务时binlog事件里已经带着GTID从库直接记录这个GTID不会重新生成。所以同一笔事务在主库和所有从库上拥有同一个GTID这是GTID复制的核心依据。主从两边各自维护自己的gtid_executed集合它代表“本实例已执行过的事务集合”。从库就是通过对比自己和主库的gtid_executed知道该向主库要哪些缺失的事务。系统里还有两个状态需要区分gtid_executed是已执行的事务集合gtid_purged是已被清理比如binlog被purge不能再提供的事务集合。从库连接主库时如果发现自己需要的GTID落在主库的gtid_purged里就会报错因为主库已经拿不出那份binlog了。4.2 用AUTO_POSITION搭建复制GTID模式下的复制配置比文件位点简洁得多。主从节点使用以下配置gtid_modeON enforce_gtid_consistencyON log_binON log_slave_updatesON server_id1 # 从库改成不同值主库创建复制账号的过程和异步复制一样。从库执行CHANGE REPLICATION SOURCE TO SOURCE_HOST192.168.1.10, SOURCE_PORT3306, SOURCE_USERrepl, SOURCE_PASSWORDYourStrongPass2024, SOURCE_AUTO_POSITION1;关键就在SOURCE_AUTO_POSITION1。这个参数让从库在连接时把自己的gtid_executed集合发给主库主库算出缺失的GTID区间从自己的binlog中找出对应事务推送过来。整个过程不需要指定SOURCE_LOG_FILE和SOURCE_LOG_POS从库也不会重复执行已经执行过的事务。GTID对切换的改善是革命性的。主库A宕机把从库B提升为新主再把旧从库C指向B时不需要去查B的binlog位点。C直接把gtid_executed交给BB自动补发C缺失的事务C也自动忽略自己已经有的。整个切换过程从“手工找位点”变成了“自动按GTID补差”省掉最容易出错的环节。4.3 老环境从文件位点复制切换到GTID的要点已经运行在文件位点复制下的老环境如果不重构数据库就能平滑切到GTID。官方推荐按以下模式缓慢切换核心原则是先在宽松模式下跑一段时间确认没有匿名事务产生再逐渐收紧。# 第一步宽松模式 gtid_modeOFF_PERMISSIVE enforce_gtid_consistencyWARN观察一段时间日志确认没有GTID一致性相关的WARN。然后将主库和所有从库按顺序切换# 第二步允许生成GTID但旧匿名事务仍允许存在 gtid_modeON_PERMISSIVE enforce_gtid_consistencyON等主库完全没有匿名事务产生后最终切换到严格模式gtid_modeON切换过程中要逐个节点操作主库优先。全程都要保证enforce_gtid_consistencyON否则可能产生违反GTID一致性的事务比如在事务中改临时表、CREATE TABLE ... SELECT这类语句后续会引发同步中断。这一步最容易翻车的地方是直接一步到位把gtid_mode从OFF翻到ONMySQL会直接拒绝或丢匿名事务。生产方式切换前建议先在测试环境跑一遍完整流程把时间节点记录下来。4.4 GTID模式下的报错处理经验GTID环境里最常遇到的报错和处理方法我总结几个实际的跳过某个导致复制中断的坏事务。老办法SET GLOBAL sql_slave_skip_counter1在GTID下已经不好用了。正确思路是把这个事务的GTID手动写入从库的gtid_executed让它不再重复执行STOP REPLICA; SET SESSION.GTID_NEXT3f3f2ea5-5a77-11ee-8b88-005056c00001:42; BEGIN; COMMIT; SET GTID_NEXTAUTOMATIC; START REPLICA;这种方式本质是往gtid_executed里补一条“已执行”记录后续这个事务就不会被再次拉取。注意只能在你确认该事务可以不执行的前提下这样操作如果事务包含了必须执行的数据变更盲目跳过会造成主从数据不一致。从库本地产生过多余写入导致gtid_executed比主库还大。此时从库连接主库后会报错因为主库找不到比从库更新的GTID。处理方式是先把从库的多余事务清掉让它的gtid_executed收敛回主库的子集再重新开启复制。这个操作比较危险建议先用备份把从库重置而不是手工改gtid_executed。ERROR 3546。修改gtid_purged时gtid_executed必须为空否则拒绝执行。常见于从库曾经执行过本地事务残留了GTID重置时要把gtid_executed清空后才能设置gtid_purged。GTID不是万能药它仍然不保证从库回放完成也不保证多主写入不冲突。但作为复制基础它已经把运维最头疼的位点问题解决了。5. MGR组复制Paxos基础上的自动化复制架构MGR全称Group Replication是MySQL 8.0提供的组复制方案。它和传统复制最大的区别在于引入了组通信层和共识协议让多个节点组成一个复制组组内的事务变更通过多数派确认后才提交。5.1 MGR和传统复制解决的是不同的问题传统复制包括GTID本质上还是“一个主库对多个从库”的结构主库故障时切换动作本身需要人工介入或者依赖外部高可用组件。MGR则把多个节点组成一个组组内任何一个节点提交事务都要把binlog事件广播给组内所有节点经过certify阶段做冲突检测再由多数派节点确认之后事务才真正提交。这套机制解决了几个传统复制解决不了的问题故障切换自动化。单主模式下主节点故障后组内自动选出新主业务无需人工干预也不需要外部脚本判断应该把哪台从库提升为主。数据一致性更强。多数派确认机制让提交成功的事务至少在多数节点上留下了记录配合半同步可以实现接近零丢失。多主写入能力。多主模式下多个节点同时接受写入配合certify阶段做冲突检测没有冲突的事务都能正常提交。MGR底层依赖组通信系统和Paxos共识协议。组内每个节点通过组通信层广播事务和接收确认节点之间的心跳、状态切换、选主都通过这个层完成。5.2 单主模式与多主模式的取舍MGR有两种运行模式。单主模式默认下组内只有一个节点是primary接受读写其余节点是secondary只读。primary故障后组内根据规则自动选一个新primary整个过程对业务只表现为一次主库切换。单主模式胜在简单事务不需要做跨节点冲突检测性能也更稳定绝大多数生产环境建议用这个。多主模式下所有节点都可以写入。因为多个节点可能同时修改同一行certify阶段要对事务做冲突检测。两个节点并发更新同一行必然有一个事务被回滚。这个回滚在客户端看起来就像随机死锁一样对业务很不友好。所以我个人在绝大多数场景下推荐单主模式多主模式只适合明确知道写入不会互相冲突的业务比如按租户分片路由。5.3 搭建MGR的关键参数和步骤MGR对基础参数有硬性要求gtid_modeON、enforce_gtid_consistencyON、log_binON、log_slave_updatesON。这些是GTID复制的基本配置MGR在GTID基础上构建所以一个都不能少。典型的my.cnf配置段server_id1 gtid_modeON enforce_gtid_consistencyON log_binON log_slave_updatesON plugin_load_addgroup_replication.so group_replication_group_nameaaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee group_replication_start_on_bootoff group_replication_local_address192.168.1.10:33061 group_replication_group_seeds192.168.1.10:33061,192.168.1.11:33061,192.168.1.12:33061 group_replication_bootstrap_groupoff各节点的server_id和group_replication_local_address不同。group_replication_group_name是一个UUID用SELECT UUID()生成组内所有节点必须一致。MGR需要专用复制账号并且权限要求比普通复制账号多CREATE USER repl% IDENTIFIED BY YourStrongPass2024; GRANT REPLICATION SLAVE, REPLICATION CLIENT, GROUP_REPLICATION_ADMIN, BACKUP_ADMIN ON *.* TO repl%;GROUP_REPLICATION_ADMIN是MGR管理权限BACKUP_ADMIN是MGR内部做distributed recovery时需要的权限少了它启动会直接报权限不足。启动顺序是第一个节点先执行SET GLOBAL group_replication_bootstrap_groupON;然后执行START GROUP_REPLICATION;成功后再立即把bootstrap设置为OFF防止后续节点误加入创建新的组。第二个、第三个节点直接执行START GROUP_REPLICATION;它们会根据group_replication_group_seeds找到组内已存活的节点通过distributed recovery把缺失的数据补齐然后加入组。验证组状态SELECT * FROM performance_schema.replication_group_members;正常状态是组内所有节点的MEMBER_STATE为ONLINEMEMBER_ROLE中只有一个是PRIMARY单主模式下。5.4 MGR的性能代价和边界限制MGR不是免费午餐。每个写事务在提交前都要广播到组内经过certify和多数派确认这比单机提交多了至少一次网络往返。跨机房部署时网络RTT每增加1毫秒写入延迟大约就增加1毫秒所以MGR对组内节点间的网络质量要求非常高强烈不建议跨地域组网。MGR还有几个公认的限制仲裁机制要求组内多数节点存活才能正常服务。三节点组挂掉一个还能工作挂掉两个剩余一个节点无法满足多数派整个组写不可用。所以生产环境至少部署3个数据节点条件允许再加一个仲裁节点。多主模式下并发更新同一行的事务回滚率会明显上升。单主模式则没有这个问题这也是我推荐单主的主要原因。大事务会放大广播开销。一个几百MB的事务广播到组内所有节点占用大量网络和内存延迟会飙升。生产环境要额外监控大事务。MGR对部分SQL特性有限制比如禁用了某些功能部署前要对照官方文档确认业务SQL的兼容性。MGR适合对RPO要求高、希望自动选主、写并发有一定控制能力的场景。如果业务写入量巨大半同步已经能满足需求选MGR反而可能增加不必要的复杂度。6. 生产落地组合打法、监控指标与切换自查原理层面聊完最后落到生产环境该怎么选型、怎么监控、怎么安全切换。这部分是我多年运维下来沉淀的自查方法不保证放之四海皆准但至少能让心里更有底。6.1 按业务容忍度做选型组合我会按RPO需求把环境分成三类如果业务能接受秒级甚至分钟级延迟比如纯读写分离、报表查询、离线分析异步复制加GTID就够了。配置最简单性能损耗最小故障切换时接受可能丢少量数据。如果业务对数据丢失零容忍半同步加GTID是底线配置。半同步堵住了丢binlog的窗口GTID让切换时不需要找位点两者配合已经能覆盖绝大多数金融、交易类场景。这种组合下切换动作仍然要人工或依赖外部高可用组件但切换难度和风险比纯异步低一个量级。如果业务希望主库故障后自动化切换且能接受多节点组网和网络开销上MGR单主模式。它对运维自动化帮助很大但要注意仲裁节点存活和网络稳定性。6.2 监控指标与告警清单无论选了哪条路线以下指标都应该纳入监控复制线程状态。Replica_IO_Running、Replica_SQL_Running不为Yes时立即告警这是复制中断的第一信号。延迟。Seconds_Behind_Master超过阈值告警同时对比Read_Master_Log_Pos和Exec_Master_Log_Pos的差值判断瓶颈在IO接收还是SQL回放。半同步状态。重点看Rpl_semi_sync_source_status是否从ON掉到OFF以及Rpl_semi_sync_source_no_tx是否持续增长。这条至关重要因为半同步退化是无声的。GTID差异。定时比对主库和从库的gtid_executed计算差集大小。GTID差集持续增长基本等同于复制在追不上等业务高峰期就会爆发延迟告警。MGR状态。performance_schema.replication_group_members里出现非ONLINE状态节点立即告警同时关注replication_group_member_stats里的事务冲突计数。6.3 主从切换自查清单最后分享一份我实战中用的切换前自查清单。每次演练和真实切换都按这个顺序执行能砍掉大部分人为失误1. 停止主库写入应用切流或SET GLOBAL super_read_onlyON;确保旧主不再接收新事务。 2. 检查候选从库REPLICA_SQL_Running为YesRead_Master_Log_Pos和Exec_Master_Log_Pos一致relay log已全部回放。 3. 检查GTID差集新主库的gtid_executed必须覆盖旧主库已提交的全部GTID否则会丢数据。 4. 提升新主执行STOP REPLICA; RESET REPLICA ALL;然后SET GLOBAL read_onlyOFF;确认新主可写。 5. 指向新主其余从库CHANGE REPLICATION SOURCE TO指向新主使用SOURCE_AUTO_POSITION1。 6. 旧主降级原主库STOP REPLICA并作为从库指向新主确认数据追平后再开放只读业务。 7. 数据校验切换完成后用校验工具对比关键表数据确认无差异。这套流程里最容易出问题的是第2步和第3步。很多人切换时只看了Seconds_Behind_Master为0就想切但半同步环境下这个值本身就可能不准确。真正的底线是候选从库执行过旧主库binlog里的每一个已提交事务没有谁比gtid_executed集合更能说明这个问题。我个人在实际运维中还有一个习惯每次切换演练都记录“从停写流量到恢复写入”的总耗时并且把每一步输出都存档。这个习惯能在真正故障时极大缓解焦虑因为你知道最坏情况的耗时大概是多少也清楚哪一步容易卡住。复制这件事原理吃透了剩下的就是反复演练把不确定性一步步消灭在平常。