首页 / 资讯中心 / 文章详情

MySQL主从复制实战:从搭建到调优与故障切换的完整指南

MySQL主从复制实战:从搭建到调优与故障切换的完整指南 ★ FEATURED ARTICLE
说起来挺惭愧我第一次做 MySQL 主从并不是什么高瞻远瞩的架构规划而是被线上压出来的某天下午库的 CPU 突然顶到 90% 多业务方反馈查询越来越慢一看监控单库读流量已经把磁盘 IO 快打满了。老板扔过来一句话加一台从库把读流量分流出去。我心想这不简单嘛克隆实例、改配置、change master半天搞定。结果第二天就被现实教育了。从库延迟越拉越大业务查询走从库反而更慢主库上开发同学误更新了一张大表从库跟着重放了半天一堆读请求全卡住。那时候我才意识到主从复制不是执行几条 SQL 那么简单binlog 格式、位点管理、半同步策略、并行复制参数每一层都藏着取舍和坑。这篇文章我不打算讲教材式的概念而是把我从“只会搭主从”到“能把主从调稳”的完整过程拆开来说。会覆盖主从到底解决了什么问题、复制链路底层机制、搭建时的参数基线和正确姿势、延迟的根因排查与优化以及故障切换前那些容易被忽略的细节。适合已经会基本 MySQL 操作、想深入了解主从架构或者正准备给业务加只读从库的同学参考。1. 明明只是“加一台从库”为什么后来变成一场架构改造1.1 我先说结论主从架构真正解决的是什么主从复制在日常实践中解决的并不是“提升写入性能”而是三件事分摊读压力、提供备份/容灾基础、支撑数据库滚动变更。读压力这条很好理解。大多数业务系统都是读多写少在线商城、内容平台、SaaS 后台读写比例轻松超过 10:1。单库一台机器CPU 和磁盘 IO 是有限的当读流量涨上来同一台机器既要写 binlog、刷新脏页又要响应大量查询互相争抢资源。挂一台从库把 select 全部引流过去主库专心处理写入压力立刻下来。备份容灾这块经常被忽略。很多团队做备份用的是 mysqldump 或 xtrabackup 直接在主库上跑高峰期会对主库产生明显的额外负载。有了从库备份可以全部拿到从库执行既不影响主库又能保证备份数据的相对新鲜。更重要的是如果主库机器硬件损坏、文件系统出问题一个同步正常的从库可以直接顶上RTO 可以做到分钟级而不是从备份恢复到天级。滚动变更是我后来才体会到的。像大表加索引、改表结构这种 DDL如果直接在主库执行锁表期间线上写入直接卡死。有从库以后可以先在从库执行观察从库上查询性能和复制延迟是否异常再找个低峰期切库升级。虽然 MySQL 8.0 的在线 DDL 已经比 5.7 强很多但“先在从库试跑一遍”的流程对降低变更风险还是很有价值的。1.2 哪些场景别急着做主从说完了能干什么也得说说不能干什么。我见过不少团队把主从当成“数据库性能优化万能药”结果越搞越复杂照样出问题。下面几类场景上主从前要心里有数。写压力大的业务加从库解决不了根本问题。主从复制天生是单向的所有写入必须到主库从库再通过 binlog 异步重放。如果你的瓶颈是写并发高、主库 commit 速度跟不上那么加再多的从库主库该堵还是堵。这种场景更适合先做读写分离之外的优化比如调整事务粒度、批量写入、升级硬件甚至考虑分库分表。单张大表查询极慢的场景主从也救不了。从库和主库的数据结构完全相同主库上慢的 SQL比如扫描 5000 万行的 join从库上一样慢。主从解决的是资源争抢问题不是 SQL 效率问题。有从库之后慢 SQL 只是换了一台机器继续慢同时还把从库的复制链路拖住让从库延迟变大影响所有走从库的读请求。核心数据要求强一致的场景纯异步主从要慎用。这是主从架构里最隐蔽的坑默认情况下主库提交事务后从库什么时候同步完主库完全不关心。如果业务要求“写完立刻能读到最新数据”而代码里走的是从库就可能在极端时刻读到旧数据。这时候要么把强一致的请求强制路由到主库要么升级为半同步复制后面我会专门讲要么直接上 MySQL Group Replication 这类强一致方案。说白了主从是在“资源与成本”之间做权衡不是提升单机极限的方案。想清楚这一点后面做参数调优和架构演进时方向才不会跑偏。2. 复制链路的底层机制binlog、GTID 与半同步的取舍我对主从的理解真正上一个台阶是从搞明白复制链路底层开始的。很多朋友配主从只会复制粘贴命令出了延迟或丢数据问题完全没思路就是因为没搞明白主库到底往从库传了什么、从库又是怎么应用的。2.1 binlog 三种格式到底选哪个binlog 是主从复制的数据源头。它有三种格式STATEMENT、ROW、MIXED它们的差异对比我列在下面。格式记录内容优点缺点STATEMENT记录原始 SQL 语句日志体积小容易阅读对非确定性函数NOW()、UUID()、临时表、大批量 UPDATE 可能导致主从不一致ROW记录每一行数据变更前后内容数据一致性最强几乎所有变更都能精确重放日志体积可能膨胀大事务会产生巨量 binlogMIXEDMySQL 自动判断默认 STATEMENT存在风险时切换到 ROW折中方案平时日志小对风险判断不透明偶尔会意外切到 ROW 导致日志暴涨我个人的结论是MySQL 5.7 以上直接选 ROW不要犹豫。早期版本选 STATEMENT 是因为日志体积小、同步快。但它的致命问题是容易产生主从数据不一致。最典型的就是 sysdate 这种非确定性函数主库执行的时候是一个时间从库重放的时候可能已经过了几毫秒两个库的数据就对不上了。在金融、电商这类对数据准确性要求极高的场景这种“偶尔不一致”是完全不能接受的。ROW 格式虽然日志体积大但记录的是“哪张表的哪一行从什么值变成了什么值”不管 SQL 在从库上执行多少次结果都是一样的。而且 ROW 格式还有一个额外好处可以用 binlog 做误操作恢复。比如开发同学不小心 DELETE 了全表STATEMENT 格式的 binlog 里只有一条 DELETE FROM 语句你想恢复都不知道原来的数据长什么样ROW 格式的 binlog 保底可以通过工具逆向出删除前的数据至少有个后悔药。2.2 GTID 让位点管理退居二线早年间配主从最烦的就是位点binlog file position。每次要从主库 SHOW MASTER STATUS 看当前写到哪个 binlog 文件第几个偏移量然后让从库从那个位点开始拉。问题在于这个位点会不断变化一旦从库断连时间太久、binlog 已经被清理位点就找不回来了得重新做全量备份再补增量非常痛苦。GTIDGlobal Transaction Identifier解决的就是这个问题。每个事务在提交时会被分配一个全局唯一的事务 ID格式是server_uuid:transaction_id从库只需要告诉主库“我已经执行到哪些 GTID”主库就能自动推算出需要发送哪些缺失的事务。整个定位过程由 MySQL 自动完成不再依赖容易过期的文件偏移量。对比一下两种配置方式# 老方式指定 binlog 文件与位置 CHANGE MASTER TO MASTER_HOST10.0.0.2, MASTER_USERrepl_user, MASTER_PASSWORDyour_password, MASTER_LOG_FILEmysql-bin.000023, MASTER_LOG_POS154; # GTID 方式让 MySQL 自动拉齐 CHANGE MASTER TO MASTER_HOST10.0.0.2, MASTER_USERrepl_user, MASTER_PASSWORDyour_password, MASTER_AUTO_POSITION1;GTID 模式下管理主从关系最省心的就是 failover。原来主库挂了你要去日志里扒最后到底同步到哪个 binlog 位点再把从库指向新主库现在直接给 CHANGE MASTER TO 加一个MASTER_AUTO_POSITION1MySQL 自己会判断从哪个事务开始拉。这也是我强烈建议新集群直接用 GTID 的原因。2.3 异步模式下的数据窗口与半同步救场默认的 MySQL 复制是异步的主库事务提交完binlog 写进本地磁盘就返回客户端“成功”至于从库有没有收到、收到后又有没有执行主库完全不关心。异步模式的好处是主库性能损耗极小坏处也很明显主库突然宕机时可能在主库上提交了一部分事务但从库还没来得及同步新主库提升后这部分事务就丢了。业务表现为“刚刚写入的数据莫名其妙消失了”。解决这个问题的常用方案是半同步复制Semisynchronous Replication。它的核心逻辑是主库在提交事务时必须等待至少一个从库确认“我已经收到了这个事务的 binlog”才能向客户端返回事务提交成功。相当于把“写入成功”的含义从“写到自己磁盘”升级为“还有一个副本收到了”大大缩小了数据丢失窗口。启用半同步复制需要主库和从库都加载对应插件# 主库 INSTALL PLUGIN rpl_semi_sync_master SONAME semisync_master.so; SET GLOBAL rpl_semi_sync_master_enabled 1; SET GLOBAL rpl_semi_sync_master_timeout 1000; # 从库 INSTALL PLUGIN rpl_semi_sync_slave SONAME semisync_slave.so; SET GLOBAL rpl_semi_sync_slave_enabled 1;参数rpl_semi_sync_master_timeout意思是如果主库等待从库 ACK 超过 1000 毫秒仍没有回应就自动退化为异步复制。这个退化机制很关键——半同步的初衷是保证“尽量不丢数据”但如果从库网络抖动导致每条写入都等 1 秒主库写入性能会被直接拖垮业务就彻底卡死了。所以实际部署中这个 timeout 一般设置 1000ms 左右同时也是在提醒你半同步能缩小丢失窗口但替代不了主从延迟监控和故障切换预案。MySQL 8.0.26 开始官方逐步推进用“同步连接故障转移”机制替代传统半同步插件存量 5.7 环境里半同步插件依然非常普及。我的建议是业务写入要求较高、又能容忍一点点性能损耗的话优先开半同步如果从库跨机房且网络质量不好可以先不开把精力放在监控和快速切换上。3. 搭建与参数基线照着这套配置能少踩一半坑这节是最“抄作业”的部分。我会给出一套我自己实践下来比较稳的参数基线和完整操作流程并解释每一步为什么这么做。注意基线不是让你无脑照抄而是让新手有个可靠起点真正上线前一定要根据实际业务场景微调。3.1 主库配置要点先看主库的my.cnf核心参数[mysqld] server-id 100 log_bin mysql-bin binlog_format ROW sync_binlog 1 gtid_mode ON enforce_gtid_consistency ON binlog_expire_logs_seconds 86400server-id整个复制集群中每个节点必须唯一。这个最容易踩坑很多人复制了配置文件忘了改两个库的 server-id 一样从库连接后报错或者复制直接中断。sync_binlog 1每次事务提交都把 binlog 刷到磁盘。这是一个安全性和性能的权衡项。设为 1 能最大程度保证主库即便崩溃也不丢 binlog代价是每条提交多一次磁盘 fsync。如果从库数量多、业务写入量大可以把主库的 sync_binlog 调成 0 或者 N但必须接受断电时可能丢失最近 N 个事务的 binlog 的风险。这个参数建议优先安全尤其生产环境别轻易降到 0。binlog_expire_logs_secondsbinlog 保留时间。如果从库宕机时间超过这个值重启后可能会由于 binlog 已清理而需要重新全量同步。建议根据业务可接受的恢复时间设置一般 7 天比较稳妥。3.2 从库配置要点从库的my.cnf要多几个参数[mysqld] server-id 200 log_bin mysql-bin binlog_format ROW gtid_mode ON enforce_gtid_consistency ON log_slave_updates ON relay_log relay-bin read_only ON slave_parallel_type LOGICAL_CLOCK slave_parallel_workers 8log_slave_updates ON让从库把重放的事务也记录到自己的 binlog。这个参数在“从库再往下挂从库”或者“从库提升为主库”时是必需的。很多初学者没开这个导致级联复制直接失败。read_only ON从库只允许复制线程写入应用层的插入、更新、删除全部拒绝。这是防止“有人手滑往从库写数据”的最后一道防线。注意它拦不住super权限账号所以即使是运维账号日常也不要用 super 账号连接从库去改数据。slave_parallel_type和slave_parallel_workers从库并行复制参数设置好以后从库才能用多个线程并行重放不同事务而不是单线程慢慢磨。这部分我放第四节专门讲。3.3 数据初始化与 CHANGE MASTER TO 的正确姿势新从库的复制最理想的状态是从一个一致性的基础快照开始而不是直接 CHANGE MASTER TO 让它从头拉。原因很简单如果主库 binlog 已经写了几百 GB从库要从第一个 binlog 开始解析重放得追到猴年马月。实操上我一般这么走第一步在主库用 xtrabackup 做全量备份这个工具能把 InnoDB 在备份时刻的 binlog 位点信息一并记录到 xtrabackup_binlog_info 文件里。如果你是 GTID 模式文件里记录的是该时刻的 GTID 集合。第二步把备份文件拷贝到从库机器用xbstream -x解压后执行innobackupex --apply-log做恢复准备最后把数据目录里的文件放到位。第三步启动从库实例确保配置里的gtid_modeON。然后执行CHANGE MASTER TO MASTER_HOST10.0.0.2, MASTER_USERrepl_user, MASTER_PASSWORDyour_password, MASTER_AUTO_POSITION1; START SLAVE;这里我用的是 GTID 自动定位从库会自动从全量备份记录的 GTID 集合之后继续同步完全不用关心 binlog 文件名和偏移量比起老方式的 CHANGE MASTER 省心太多。如果你的环境还是 MySQL 5.7 且没开 GTID那就只能记位点了备份完成后在主库执行 SHOW MASTER STATUS记录 File 和 Position再用 CHANGE MASTER TO 指定MASTER_LOG_FILE与MASTER_LOG_POS。每次主从切换都得重新记录一遍非常容易出错这就是我为什么反复强调新环境尽量一步到位上 GTID。3.4 复制状态验证别只看“两个 Yes”配完从库大多数人第一反应就是执行SHOW SLAVE STATUS\G看到Slave_IO_Running: Yes和Slave_SQL_Running: Yes就觉得万事大吉。我一开始也这样直到栽了跟头才明白这两个字段是“进程在不在跑”不等于“数据追平了”。真正需要关注的字段至少还有这几个字段含义重点关注原因Seconds_Behind_Master从库落后主库的秒数延迟的直接指标但下面详细说它的坑Master_Log_File/Read_Master_Log_PosIO 线程拉取到的 SQL 线程已经执行的 binlog 位置对比它能看出 IO 线程和 SQL 线程哪边卡住Retrieved_Gtid_SetIO 线程已经拉取的 GTID 集合与 Executed_Gtid_Set 对比判断增量数据是否都执行完Executed_Gtid_Set从库已经执行完成的 GTID 集合切换主库前必须保证它和主库的集合对齐Last_IO_Errno/Last_SQL_Errno最近一次 IO/SQL 线程错误码复制中断的真实原因往往写在这两栏Last_SQL_Error最近的 SQL 错误文本比如主键冲突、表不存在都会在这里显示我自己验证复制有没有真正跑起来通常分三步在主库执行一条带日期的写操作创建一个临时测试表插入几条记录到从库执行同样的 select 查询确认数据已经出现查看SHOW SLAVE STATUS中Seconds_Behind_Master在持续变小并最终趋近 0。如果确认这些都正常再回过去看两个 Yes 才有点意义。不然两个 Yes 只是代表线程活着复制链路本身是不是健康还得靠数据说话。4. 主从延迟从根因排查到并行复制的完整优化路线主从延迟是主从架构里最常被问、也最让人头大的问题。一次线上大事务从库可能要落后主库几百秒查询走从库读到的全是旧数据业务投诉“数据不对”的工单能堆满一个屏幕。下面我把延迟的监控指标、根因和优化手段完整串一遍。4.1 Seconds_Behind_Master 的真相与三个误区先把这个指标讲清楚。Seconds_Behind_Master的官方含义是从库 SQL 线程当前执行的事务的时间戳和 IO 线程最近拉取到的 binlog 事件时间戳之间的差值。看起来很简单实际有以下三个误区第一个误区Seconds_Behind_Master 0不代表数据绝对强一致。它只能表明 SQL 线程已经执行到 IO 线程最新拉取的位置但 IO 线程拉取的 binlog 本身是异步的。主库刚提交的事务从库 IO 线程可能还没拉过来这时候延迟显示是 0实际从库数据还是旧的。第二个误区主库突发大事务时这个值非常容易被低估。比如主库执行了一个跑了 3 分钟的大事务它产生的是一个大 binlog 事件带一个 3 分钟前的时间戳。从库开始重放这个事件时Seconds_Behind_Master会瞬间跳到很大而重放过程中如果 IO 线程实际上早已把所有 binlog 拉到本地从库就会显示延迟正在缩小实则 SQL 线程还在吭哧吭哧干活。第三个误区复制中断时这个值可能显示为 NULL。IO 线程断掉后从库没有新 binlog 可以比较时间戳MySQL 干脆返回 NULL。很多人看到延迟显示 NULL以为数据同步正常结果其实是复制早就断了。所以我的实践习惯是Secondary 指标只看趋势不把它当唯一告警依据。真正有用的监控是把Seconds_Behind_Master和主库 binlog 最新时间、从库执行到的 binlog 时间组合起来看再配合下面的根因分类去定位问题。4.2 延迟根因六类先定位再动手遇到延迟不要急着调参数先搞清楚是哪种原因。我把实际中遇到的根因归成六类。第一类大事务。这是最常见的。主库上一条UPDATE或DELETE影响了几百万行ROW 格式下会产生几 GB 的 binlog从库要一条条重放。主库执行 5 分钟从库执行半小时并不奇怪。这类问题的最优解不是调从库参数而是从应用层拆分大事务把影响行数降下来。比如历史数据清理用分批删除代替一次性 delete 全表-- 每次只删 2000 条配合 sleep 控制负载 DELETE FROM orders WHERE create_time 2023-01-01 LIMIT 2000;第二类DDL 操作。ALTER TABLE 在主库上锁表会引起写入阻塞而在从库上重放同样的 DDL 时同样会锁。很多团队在主库上跑完在线 DDL发现从库延迟瞬间飙升就是因为从库也执行了一遍同样的 ALTER。这类延迟没有银弹只能在低峰期执行并确保从库的lock_wait_timeout、innodb_lock_wait_timeout设置合理不让一个 DDL 的锁把复制线程拖死。第三类从库自身承担了额外负载。从库上跑了定时备份、数据分析报表查询这些操作会占用大量 CPU 和磁盘 IO压缩了复制线程的资源空间。这类问题最容易被忽视因为从库上往往塞了一堆“只读”任务。解决思路是从库就是复制和读流量专用的备份、报表尽量放在独立的小型分析实例上或者用资源隔离手段限制后台任务的 CPU 使用。第四类从库被无脑写入。虽然read_onlyON能挡掉大部分写入但如果应用账号恰好有 super 权限还是可能绕过。往从库写入的数据等主库那边一变更就可能触发主键冲突SQL 线程直接停止延迟瞬间拉大。我处理过一次类似事故排查半天发现是从库上排了个定时任务用高权限账号跑了更新语句。遇到这类问题第一件事是检查从库read_only第二件事是查SHOW PROCESSLIST里有没有业务来源的写入线程。第五类复制线程本身是单线程瓶颈。老版本 MySQL 从库应用 binlog 是单线程的主库写入并发一高从库就追不上。这个问题可以通过并行复制解决见下一小节。第六类硬件和网络差异。从库机器磁盘比主库慢网络带宽不足io_thread 拉 binlog 就慢也会造成延迟。这类问题往往被各种参数调优掩盖了其实换块 SSD 或者加大带宽就解决了。4.3 并行复制让从库“多线程干活”MySQL 5.7 之后的从库并行复制是解决单线程瓶颈的最关键方案。核心思路是把从库复制线程分成一个协调线程和多个工作线程让没有依赖关系的事务可以并行应用而不是一个一个排队执行。相关参数主要是下面三个slave_parallel_type LOGICAL_CLOCK slave_parallel_workers 8slave_parallel_type决定了事务之间的依赖判断方式。推荐设置为LOGICAL_CLOCK这是基于事务提交时间提交时钟戳来划分并行组的主库上同时提交的事务会被认为没有互相冲突可以在从库并行应用。相比老旧的DATABASE模式按不同库并行但绝大多数业务都是单库等于白设LOGICAL_CLOCK 在单库多事务场景下效果明显得多。slave_parallel_workers是工作线程数量。并不是越大越好。我在实践中发现 8 是一个比较稳妥的起点如果从库 CPU 核数足够多、事务又比较简单可以调到 16但如果设置过大每个工作线程之间的锁竞争、提交时的 fsync 竞争会让总吞吐反而下降。调优建议是先把 workers 从 4 调到 8、16观察从库 CPU 利用率和Seconds_Behind_Master变化找到那个“再调收益也不明显”的拐点。MySQL 8.0 还多了一个参数binlog_transaction_dependency_tracking可以把它设为WRITESET。LOGICAL_CLOCK 模式下MySQL 默认按提交时间判断依赖WRITESET 则更进一步基于事务实际修改的行集合去判断能不能并行能识别出大量表面有先后、实际操作不同行的无冲突事务。这个参数在 8.0 上对提升并行度很有帮助。从库配置改完后需要重启 MySQL 才能生效。改完后的验证方式也很直接执行SHOW SLAVE STATUS\G看Slave_SQL_Running_State是否出现“Waiting for dependent transaction to commit”之类的等待状态再用performance_schema里的表看各 worker 的实时状态SELECT WORKER_ID, RELAY_LOG_FILE, RELAY_LOG_POS, LAST_APPLIED_TRANSACTION FROM performance_schema.replication_applier_status_by_worker;如果能看到不同 worker 各自在处理不同事务就说明并行复制真正在起作用了。4.4 应用层配合大事务拆分与读写路由调完参数不代表万事大吉应用层配合才是大延迟场景的治本之道。大事务拆分的核心原则是一个事务尽量只影响一小撮数据把它拆成多个小事务每次提交间隔几十毫秒。我举一个真实的例子某系统每天晚上要清理 30 天前的 session 日志开发同学写的是DELETE FROM session_log WHERE create_time ...一次性删几百万行。这 SQL 在主库执行 20 秒从库重放却花了 3 分钟导致每天晚上延迟都飙到 200 秒。后来改成存储过程分片删除DELIMITER $$ CREATE PROCEDURE sp_cleanup_session_log() BEGIN DECLARE affected INT DEFAULT 1; WHILE affected 0 DO DELETE FROM session_log WHERE create_time DATE_SUB(NOW(), INTERVAL 30 DAY) LIMIT 2000; SET affected ROW_COUNT(); DO SLEEP(0.1); END WHILE; END$$ DELIMITER ;改造之后主库每次只删 2000 行binlog 只有 2000 条变更记录从库重放压力急剧降低延迟稳定在 1 秒以内。读写分离路由方面常规做法是在应用层或者中间件层区分读和写写 SQLINSERT/UPDATE/DELETE强制走主库读 SQL 默认走从库但对一致性要求高的核心会话比如下完单马上要查订单详情强制在主库读一次。如果玩得更细可以在事务尚未提交时把该连接的后续查询都固定到主库避免“同一请求内先写后读”产生不一致。这些代码层的取舍决定了从库在业务眼里到底是一个“只读镜像”还是一个“可被查询的缓存”。想清楚了后续优化才有抓手。5. 故障切换和一致性保障主从不是终点可用性才是搭好主从、搞定延迟只是开始。真正让人睡不着的是主库突然宕机那一刻。下面说说故障切换前那些必须提前想清楚的事。5.1 切换前的一致性校验切换主从的前提是“从库上的数据已经和主库对齐”。如果从库早就落后了几千个事务你直接把它提升为新主那旧主库最新提交的数据就永远留在了一台挂掉的机器上这种数据丢失等于事故级别。所以我会在低峰期定期做数据一致性校验。最常用的工具是pt-table-checksum它的思路非常朴素把表按主键切分成一个个 chunk分别在主库和从库上执行相同的 CHECKSUM 查询然后比较校验值是否一致。不一致的 chunk 就会被标记出来再配合pt-table-sync修复。pt-table-checksum \ --host10.0.0.2 \ --userroot \ --passwordxxx \ --databasesorder_db \ --replicatepercona.checksums这个工具在 GTID 模式下会自动把校验查询产生的 binlog 也复制到从库不会污染校验结果。需要注意的是校验结果不相等不一定代表数据真的坏了也可能是大表在没有稳定快照的情况下主从两个库的 CHUNK 切割位置发生了偏移。真遇到不一致要结合 binlog 位置和时间点去分析是哪边先写坏了。5.2 一次标准主从切换的操作顺序主从切换属于“平时不练关键时刻必翻车”的操作。我强烈建议在测试环境把它做成演练脚本按月跑一次。标准的切换顺序大概是这样的确认要提升的原从库复制链路健康IO 线程和 SQL 线程都正常且Retrieved_Gtid_Set已经追平主库Executed_Gtid_Set。如果还有差距等待追赶直到差距为 0。在原从库上执行STOP SLAVE;停掉复制线程然后执行RESET SLAVE ALL;清空主从关系配置让它从“从库”变为可写主库。如果原主库还活着把原主库的写入停掉必要时可以把原主库设为 read_only至少让业务连接先切走。将原主库和其他老从库统一CHANGE MASTER TO ... MASTER_AUTO_POSITION1指向新主库。修改应用连接配置或中间件路由验证新主机上数据完整、复制正常后恢复对外流量。这里最容易被忽略的细节是如果原主库的 binlog 里包含了一些已经同步到从库、但还没在从库执行的事务为避免重复执行导致主键冲突切换后原主库重新挂上时使用 GTID 自动定位能比较好地规避这个问题。GTID 的好处在这里就看出来了。5.3 脑裂与选主工具能帮你多少切换到“双节点都认为自己是主库”的脑裂状态是主从架构里最危险的事故形态之一。两个主库同时接受写入互相又不同步数据分叉比单纯延迟严重得多。产生脑裂的典型场景是主库网络抖动切换工具误以为主库挂了把从库提升为新主库过一会儿旧主库网络恢复业务流量又有一部分按旧配置打到了旧主库上。两边都开始写谁都不让谁。防脑裂的核心因素有两个确认旧主库真的死了而不是网络抖动以及把对外写入口收敛到只有一个。前者通常依靠监控系统多个维度共同判定进程存活、机器重启、内部端口探测后者一般通过 VIP 或中间件配合实现。做高可用架构选型时业界常用 MHAMaster High Availability Manager和 Orchestrator 这类工具来自动化切换。它们能做到自动检测主库故障、自动选取数据最完整的新主库、自动重定向其他从库。但我必须提醒一句工具只是辅助并且要理解每个工具的行为边界尤其“探测主库存活”的判定逻辑要在自己的环境里预设好阈值防止一遇到瞬断就自动切换结果把更稳的旧主库给误杀了。5.4 再往后从主从走向 MGR 的思考如果你已经玩了两年主从发现半同步、并行复制、切换脚本都能跑通但压力不减——每次切换要人工发号施令或者工具的决策总让你不放心那确实可以考虑更进一步看看 MySQL Group ReplicationMGR。MGR 和传统主从的根本区别是它用一组节点组成一个组复制集群内部通过 Paxos 类协议做强一致投票主节点提交事务后少数节点必须确认才返回成功节点故障时组内自动选一个新主不需要外部工具介入。它吸收了半同步“等待确认”的思想又去掉了切换时的人肉环节。当然MGR 也带来新的限制要求网络稳定、要求所有节点都用 InnoDB、要求事务中没有 SELECT ... FOR UPDATE 之外的非确定性操作等等对 DBA 对事务的理解要求更高。如果只是想做“读写分离 自动故障转移”先把传统主从半同步工具跑熟再考虑 MGR 会平滑很多。主从架构本身并不是终点但它是通往更复杂架构之前最稳固的一块跳板。回头看这次“加一台从库”引发的一系列踩坑和改造我的体会是主从架构看着简单难点全在细节里而细节又分布在配置参数、数据一致性、故障切换、业务路由多个层面。如果你正准备做主从建议从这几件事开始先确认业务瓶颈确实在读侧再启用 GTID 和 ROW 格式 binlog把半同步打开为从库配置并行复制参数最后给监控系统的延迟告警设一个不误导人的阈值。这套组合拳打完主从这条路基本能走得很稳。最后再分享一个我很受用的经验不要在主从环境稳定之后就不管它了。每季度至少在测试环境演练一次主从切换写清楚每一步命令和预期结果。真到线上出故障那一刻你靠的不是“到时候再想办法”而是肌肉记忆。主从架构优化的终点不是复制不出错而是出了错你还能胸有成竹地快速恢复。
阅读完成 · 觉得有帮助?
咨询建站