
凌晨两点手机突然震动监控告警显示MySQL主库连接数异常飙升紧接着就是探活失败。等你揉着眼睛坐到电脑前打开终端准备手工切换时业务已经中断快十分钟了。这种场景对运维和DBA来说太熟悉了——主从复制架构虽然能让数据多一份保障但“主库挂了怎么切”这个问题始终像一根刺横在那里。手工切流程繁琐还容易出错不切业务就一直挂着。今天分享的这个脚本就是专门解决这个问题的基于MySQL 8.0 GTID复制架构实现主库故障后的自动检测、自动提升从库、自动迁移VIP整个过程不需要人工干预。适合正在使用MySQL主从架构、想降低故障响应时间的运维和DBA朋友参考也适合准备设计高可用方案的团队拿来当底座改造。1. 为什么需要自动化切换手工操作的痛点与方案边界1.1 手工切换到底慢在哪里MySQL主从架构本身不复杂真正复杂的是切换流程。我见过不少团队的做法是主库挂了DBA被叫醒先登录从库看落后了多少秒然后手动停复制、改只读参数、把业务连接切过去。听起来就几步但每一步都有讲究整套流程走完往往要十几分钟遇到网络波动或者从库延迟时间会更久。更要命的是这套流程不是经常演练的很多团队一年也触发不了几次故障切换。平时不练真出事了手忙脚乱命令敲错、参数记混的情况不少见。自动化脚本的价值就在这里把切换的每个步骤固化成代码把判断逻辑写成程序让机器在秒级完成检测和决策人只需要处理脚本覆盖不了的极端场景。1.2 自动切换要解决的三个核心问题设计这个脚本之前我先梳理了自动切换必须跨过的三道坎。第一道坎是故障判定。不能主库一抖动就切换那样反而会制造更多故障。需要一个合理的阈值机制连续多次检测失败才判定为真故障同时还要排除网络抖动、监控系统自身误报等情况。第二道坎是数据一致性。把从库提升为主库之前必须确认从库上的数据已经尽量追平旧主库。如果从库落后太多就直接切换会丢失大量事务。这里最理想的情况是启用了半同步复制可以最大程度保证主从数据一致。第三道坎是应用无感知。切换完成后应用该怎么找到新主库常见方案有VIP漂移、DNS切换、中间件路由。VIP漂移是最直观的一个虚拟IP挂在新主库上应用只连这个IP底层谁是主库完全透明。1.3 这套方案适用的场景与边界我要先说清楚这个脚本定位的是一主一从的经典复制架构不是全自动高可用平台。它适合以下场景业务允许秒级到分钟级的切换窗口不想引入太重的编排系统比如MHA早期版本的思路就用脚本加定时任务来实现团队成员熟悉Shell和MySQL基础操作遇到问题能人工介入处理不适用的情况也要说清楚如果主库数据损坏已经蔓延到从库、或者从库本身也挂了自动切换是无能为力的这时候必须走备份恢复流程。另外如果追求更强的数据一致性保障建议考虑MySQL官方Group Replication或者半同步复制的全面配置脚本方案更适合作为快速响应的第一道防线。2. MySQL 8.0复制架构的核心概念不懂这些写不出靠谱脚本2.1 GTID复制为什么是自动切换的地基MySQL传统的基于binlog文件位置的复制从库要记录主库的binlog文件名和偏移量切换的时候必须精确告诉从库“你从哪个位置继续拉”。这个方式在自动切换场景里很麻烦新主库可能已经产生了新的binlog旧位置信息可能对不上。GTIDGlobal Transaction Identifier从根本上解决了这个问题。每个事务在全局范围内有唯一编号从库通过GTID就知道自己已经执行了哪些事务新主库也只需告诉从库“我这里有这些GTID的事务”从库自己会判断还缺哪些并自动补拉。这意味着在主从切换时不需要手工去计算binlog位置只要确保从库已经执行完旧主库上所有已提交事务即可。我在脚本里专门加了GTID一致性检查就是基于这个原理。MySQL 8.0对GTID的支持非常成熟gtid_modeON和enforce_gtid_consistencyON是必须开启的如果还在用老版本MySQL需要先确认版本兼容性再上这套方案。2.2 主从状态到底要看哪些指标写健康检查脚本很多人第一反应是mysqladmin ping但这个命令只能说明MySQL进程活着不能说明主从复制正常。真正的检查要分两层第一层实例存活检测。用mysqladmin ping或者SELECT 1都能做到超时时间必须设得合理太短会误报太长会拖延故障发现。我实测下来--connect_timeout3配合--connect_timeout参数即mysqladmin --connect_timeout3 ping比较合适。第二层复制链路检测。在MySQL 8.0.22之后SHOW SLAVE STATUS改名成了SHOW REPLICA STATUS老命令虽然还兼容但新脚本建议直接用新写法。关键字段有这几个Replica_IO_RunningI/O线程是否在正常拉取主库binlog值为Yes才正常Replica_SQL_RunningSQL线程是否在正常回放事务值为Yes才正常Seconds_Behind_Source旧版叫Seconds_Behind_Master从库落后主库的秒数接近0才健康Last_IO_Error/Last_SQL_Error如果复制中断这里会给出原因2.3 半同步复制降低切换数据丢失的关键自动切换最大的隐忧是数据丢失。异步复制下主库提交事务和从库拉取binlog之间有时差主库突然宕机最后这一小段事务就丢了。启用半同步复制插件后主库提交事务时会等待至少一个从库确认收到binlog才向客户端返回成功。这样即使主库突发故障已提交的事务大概率已经在从库落地了。MySQL 8.0内置了半同步插件配置起来不复杂脚本方案配合半同步可以在“简单”和“安全”之间取得比较好的平衡。当然半同步也不是银弹如果主库故障瞬间半同步退化为异步模式比如从库应答超时依然存在丢失最后一小段事务的可能。所以脚本里我保留了一个可配置开关如果检测到从库落后超过阈值宁可切换失败报警也不盲目提升。3. 脚本整体设计先想清楚再动手写3.1 设计原则简单的才是可靠的自动切换脚本本身是故障场景下的“最后一道闸”它越复杂自己出问题的概率就越大。我见过有人把切换脚本写得跟个小框架似的又是多线程又是配置中心结果真到用的时候脚本因为依赖某个组件没启动而罢工了。所以我的设计原则就四条零外部依赖只用Shell、MySQL客户端、SSH、ip命令不依赖Python第三方库、不依赖Agent、不依赖外部编排服务状态可追溯每一步操作都写日志切换前后都留状态文件出了事能快速复盘安全兜底宁可失败报警绝不盲目切换。多个条件不满足就退出留给人工处理可配置化IP、账号、阈值、VIP等信息全部抽到配置文件换环境不用改脚本本身3.2 故障判定机制连续N次失败才算真的挂了刚开始写这个脚本的时候我犯过一个错误检测到主库ping不通立刻切换。结果某次网络设备闪断两台机器都活着脚本却把从库提升上去了后来数据一对比丢了几分钟的事务业务侧炸了锅。从那以后我采用了一个简单的计数机制FAIL_COUNT0每次检测失败FAIL_COUNT加1检测成功则清零。只有FAIL_COUNT达到预设阈值比如连续3次每次间隔10秒总共30秒才触发切换。30秒的等待换来的是一次更可靠的判断这笔账非常划算。另外还有一个细节阈值触发后脚本不会立即切换而是会再快速做一次二次确认。防止第一次检测失败只是因为监控命令本身超时这层双保险实测下来很有效。3.3 VIP漂移方案与脑裂防范脚本里用的VIP切换方案是整个自动切换的核心机制之一。正常情况下VIP绑定在主库机器上故障时由脚本在主库上删除VIP再到从库上绑定。这里有个关键操作是强制ARP刷新否则网络设备里的ARP缓存没更新客户端还是会把包发到老主库的MAC地址上业务依然不通。脑裂问题则是另一个重点。简单的主从切换很容易出现“双主”场景旧主库其实没真死只是网络分区让它和从库失联。脚本把从库提升为新主库后旧主库恢复两边都能写数据就乱了。我的处理方式是这样的提升从库时先通过SSH在旧主库上执行SET GLOBAL read_onlyON再配合VIP的强制抢占确保旧主库即使恢复也无法接收写流量。这个操作属于“尽最大努力”的防脑裂手段如果旧主库是完全宕机或断网SSH自然是连不上的这时候就只能靠VIP抢占和后续人工介入兜底了。4. 脚本核心实现与逐段解读4.1 配置文件换环境只改这里我习惯把脚本和配置分开配置文件里是所有环境相关的参数#!/bin/bash # /etc/mysql_failover.conf # MySQL 各节点信息 MASTER_HOST192.168.1.10 SLAVE_HOST192.168.1.11 DB_PORT3306 # 用于健康检查的账号 DB_USERrepl_monitor DB_PASSStrongPass123! # 如果用socket方式检测本机可留空 DB_SOCKET # 虚拟IP配置 VIP_IP192.168.1.50 VIP_MASK24 VIP_NICeth0 # 检测参数 CHECK_INTERVAL10 # 每次检测间隔(秒) FAILURE_THRESHOLD3 # 连续失败多少次触发切换 SECOND_CONFIRM1 # 是否开启二次确认建议开启 # 日志 LOG_FILE/var/log/mysql_failover.log STATE_FILE/var/run/mysql_failover.state # SSH密钥用于在主库上删VIP、设只读 SSH_KEY/root/.ssh/id_rsa SSH_USERroot注意健康检查账号的权限问题。MySQL 8.0默认使用caching_sha2_password认证插件如果客户端和服务器之间没有SSL首次连接时可能遇到认证问题。我的建议是给监控账号用mysql_native_password如果你能接受在老客户端兼容性上的妥协或者直接保证mysql客户端版本也是8.0以上并启用SSL。实际部署中我遇到过几次“脚本能跑但mysqladmin连不上”的情况基本都是这个原因。健康检查账号最小权限即可CREATE USER repl_monitor% IDENTIFIED BY StrongPass123!; GRANT REPLICATION CLIENT, PROCESS ON *.* TO repl_monitor%; FLUSH PRIVILEGES;REPLICATION CLIENT权限用于执行SHOW REPLICA STATUSPROCESS权限用于查看线程状态这两个就够了不用给超级权限。4.2 健康检查与复制状态检测下面是核心检测模块我拆成三个函数逻辑清楚一点后面排查也方便#!/bin/bash # /usr/local/bin/mysql_failover.sh set -u CONFIG_FILE/etc/mysql_failover.conf [ -f $CONFIG_FILE ] . $CONFIG_FILE || { echo 配置文件不存在; exit 1; } FAIL_COUNT0 log() { echo $(date %Y-%m-%d %H:%M:%S) [$1] $2 $LOG_FILE } # 主库实例存活检测 check_master_alive() { if mysqladmin --connect_timeout3 -h$MASTER_HOST -P$DB_PORT \ -u$DB_USER -p$DB_PASS ping /dev/null 21; then return 0 else return 1 fi } # 从库复制链路检测 check_slave_status() { local status_json status_json$(mysql --connect_timeout3 -h$SLAVE_HOST -P$DB_PORT \ -u$DB_USER -p$DB_PASS \ -e SHOW REPLICA STATUS\G 2/dev/null) if [ -z $status_json ]; then return 1 fi local io_status sql_status behind io_status$(echo $status_json | awk /Replica_IO_Running:/{print $2}) sql_status$(echo $status_json | awk /Replica_SQL_Running:/{print $2}) behind$(echo $status_json | awk /Seconds_Behind_Source:/{print $2}) if [ $io_status Yes ] [ $sql_status Yes ] \ [ ${behind:-NULL} -ge 0 ] 2/dev/null \ [ $behind -le ${MAX_BEHIND_SEC:-30} ]; then return 0 else return 1 fi }这里有个我从实际故障里学到的细节单纯看I/O和SQL两个线程都是Yes不代表复制真的健康。如果binlog拉取的位点长时间不动两个线程也显示Yes但数据其实已经落后了。所以一定要加Seconds_Behind_Source的落后秒数判断。我把阈值默认定在30秒这里是可调的数据量大的业务可以放宽追求强一致的建议缩到10秒以内。4.3 从库提升与VIP切换这是整个脚本的重头戏也是风险最高的部分。每执行一步都要有日志记录方便出问题后复盘# 从库提升为主库 promote_slave() { log INFO 开始执行主从切换将从库 $SLAVE_HOST 提升为新主库 # 第一步当前主库强制只读尽最大努力防脑裂 if ssh -i $SSH_KEY -o ConnectTimeout5 -o StrictHostKeyCheckingno \ $SSH_USER$MASTER_HOST \ mysqladmin --connect_timeout3 -u$DB_USER -p$DB_PASS \ -h127.0.0.1 -P$DB_PORT ping /dev/null 21 \ mysql --connect_timeout3 -u$DB_USER -p$DB_PASS \ -h127.0.0.1 -P$DB_PORT -e \SET GLOBAL read_onlyON\ \ $LOG_FILE 21; then log INFO 旧主库已设置 read_onlyON else log WARN 旧主库不可达无法设置只读依赖VIP抢占防脑裂 fi # 第二步从库停止复制并重置 mysql --connect_timeout5 -h$SLAVE_HOST -P$DB_PORT -u$DB_USER -p$DB_PASS \ -e STOP REPLICA; $LOG_FILE 21 mysql --connect_timeout5 -h$SLAVE_HOST -P$DB_PORT -u$DB_USER -p$DB_PASS \ -e RESET REPLICA ALL; $LOG_FILE 21 log INFO 从库已停止复制并清除复制信息 # 第三步从库开启读写 mysql --connect_timeout5 -h$SLAVE_HOST -P$DB_PORT -u$DB_USER -p$DB_PASS \ -e SET GLOBAL read_onlyOFF; $LOG_FILE 21 log INFO 从库已开启写入能力 # 第四步旧主库摘除VIP if ssh -i $SSH_KEY -o ConnectTimeout5 -o StrictHostKeyCheckingno \ $SSH_USER$MASTER_HOST \ ip addr del $VIP_IP/$VIP_MASK dev $VIP_NIC 2/dev/null \ $LOG_FILE 21; then log INFO 旧主库VIP已摘除 else log WARN 旧主库VIP摘除失败或无需摘除 fi # 第五步新主库绑定VIP并刷新ARP ssh -i $SSH_KEY -o ConnectTimeout5 -o StrictHostKeyCheckingno \ $SSH_USER$SLAVE_HOST \ ip addr add $VIP_IP/$VIP_MASK dev $VIP_NIC 2/dev/null; \ arping -c 3 -A -I $VIP_NIC $VIP_IP \ $LOG_FILE 21 log INFO VIP已绑定到新主库 $SLAVE_HOST # 第六步更新状态文件 echo promoted to: $SLAVE_HOST at $(date %F %T) $STATE_FILE log INFO 主从自动切换完成 }为什么要RESET REPLICA ALL因为从库一旦变成新主库它原来的复制配置信息就没有意义了。如果留着后面老主库恢复后想重新挂到这个新主库下旧的复制配置会干扰重新配置。这个命令会清掉复制账号和binlog位置信息让从库干干净净变回一个独立的可写实例。arping -c 3 -A -I这条命令很多脚本都会漏但它其实很关键。-A是无条件ARP应答用于主动告知交换机“这个IP现在是我的了”如果不刷ARP客户端和交换机可能继续把流量发到老主库的网卡上VIP漂移就白做了。4.4 主循环调度与二次确认检测和切换的核心逻辑都在主循环里我贴出来并说明设计思路while true; do if check_master_alive; then if [ $FAIL_COUNT -gt 0 ]; then log INFO 主库已恢复探测重置失败计数 fi FAIL_COUNT0 else FAIL_COUNT$((FAIL_COUNT 1)) log WARN 主库探测失败连续第 $FAIL_COUNT 次 if [ $FAIL_COUNT -ge $FAILURE_THRESHOLD ]; then if [ $SECOND_CONFIRM -eq 1 ]; then sleep 3 if check_master_alive; then log INFO 二次确认主库存活不执行切换 FAIL_COUNT0 sleep $CHECK_INTERVAL continue fi fi if check_slave_status; then log INFO 从库复制健康开始执行切换 promote_slave send_alert 主从已自动切换: $MASTER_HOST - $SLAVE_HOST exit 0 else log CRIT 从库未就绪无法自动切换请人工介入 send_alert 从库状态异常自动切换失败请立即处理 exit 2 fi fi fi sleep $CHECK_INTERVAL done二次确认这个3秒等待是我在实战中反复调出来的值太小起不到过滤瞬时抖动的作用太大又拖慢切换速度。脚本的send_alert函数里可以接入企业微信、钉钉或者短信接口模板我给一个最简单的思路send_alert() { local msg$1 # 示例调用企业微信机器人 curl -s -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY \ -H Content-Type: application/json \ -d {\msgtype\:\text\,\text\:{\content\:\$msg\}} /dev/null 21 # 也可以发邮件、短信按团队习惯来 }4.5 手动回切脚本自动切换之后还有一个必须配套做的事情故障主库修复后把它重新作为从库挂回新主库下面。这个过程叫做回切。我一般建议故障恢复后不在第一时间自动回切先让新主库跑一段时间确认稳定了再手工把老主库挂成从库。下面是我常用的回切片段# /usr/local/bin/mysql_failback.sh # 在新主库上执行把修复后的老主库重新挂为从库 NEW_MASTER$SLAVE_HOST # 切换后的新主库 OLD_MASTER$MASTER_HOST # 要恢复为从库的实例 # 1. 从库恢复后先清掉老库上的残留写角色 mysql -h$OLD_MASTER -u$DB_USER -p$DB_PASS \ -e SET GLOBAL read_onlyON; SET GLOBAL super_read_onlyON; # 2. 在老库上配置新主库 mysql -h$OLD_MASTER -u$DB_USER -p$DB_PASS EOF CHANGE REPLICATION SOURCE TO SOURCE_HOST$NEW_MASTER, SOURCE_PORT$DB_PORT, SOURCE_USERrepl_user, SOURCE_PASSWORDReplPass123!, SOURCE_AUTO_POSITION1; START REPLICA; EOF这里用的是SOURCE_AUTO_POSITION1也就是GTID自动定位。老库恢复后它本地可能有一些旧主库时期产生的事务新主库上有它没有的事务两边通过GTID自动协商新主库会把缺失的事务推过来整个过程不需要人工指定binlog位置非常省心。这也是我坚持在架构里开GTID的原因。5. 部署步骤与真实踩坑记录5.1 前置条件MySQL侧一次性配置脚本部署前MySQL侧的基础配置必须到位否则跑起来全是问题。我会把检查清单给你直接照着过一遍主库和从库都需要的配置/etc/my.cnf[mysqld] server_id 1 # 从库改成2同一复制架构下必须唯一 log_bin mysql-bin binlog_format ROW gtid_mode ON enforce_gtid_consistency ONbinlog_formatROW是8.0的默认值我提一下是因为有些老项目的配置文件里还写着STATEMENT混着GTID用容易出问题。另外如果机器要承受自动切换建议把binlog_expire_logs_seconds调大一点比如86400即1天防止从库因为落后太多binlog已经被清理而无法追平。主库上创建复制账号CREATE USER repl_user% IDENTIFIED BY ReplPass123!; GRANT REPLICATION SLAVE ON *.* TO repl_user%; FLUSH PRIVILEGES;注意我用的是账号repl_user和前面健康检查用的repl_monitor是两个账号。职责分离一下避免某个环节的权限过大。5.2 半同步复制配置推荐开启想要数据更保险建议在半同步模式上做一次配置-- 在主库和从库上分别执行 INSTALL PLUGIN rpl_semi_sync_source SONAME semisync_source.so; SET GLOBAL rpl_semi_sync_source_enabled 1; SET GLOBAL rpl_semi_sync_replica_enabled 1;要让配置永久生效需要在my.cnf里加对应的参数plugin_load_add rpl_semi_sync_source.so;rpl_semi_sync_replica.so rpl_semi_sync_source_enabled 1 rpl_semi_sync_replica_enabled 1半同步启用后主库要等待从库确认才提交切换时数据丢失概率会显著降低。有一点要注意如果主库一段时间内收不到从库的确认MySQL会自动退化为异步复制并且会在错误日志里记录。脚本监控如果发现半同步状态从ON变成OFF应该触发告警这往往意味着从库出问题了。5.3 脚本部署与启动我把脚本直接放在/usr/local/bin/和/etc/下目录结构如下/usr/local/bin/mysql_failover.sh # 主切换脚本 /usr/local/bin/mysql_failback.sh # 手动回切脚本 /etc/mysql_failover.conf # 配置文件 /var/log/mysql_failover.log # 运行日志 /var/run/mysql_failover.state # 状态文件启动方式最简单的就是用nohup加setsidchmod x /usr/local/bin/mysql_failover.sh setsid /usr/local/bin/mysql_failover.sh /var/log/mysql_failover.log 21 想更规范一点建议用systemd托管我这里给一个unit文件模板# /etc/systemd/system/mysql-failover.service [Unit] DescriptionMySQL Master Slave Auto Failover Watchdog Afternetwork-online.target mysql.service [Service] Typesimple ExecStart/usr/local/bin/mysql_failover.sh Restartalways RestartSec5 [Install] WantedBymulti-user.target然后执行systemctl daemon-reload systemctl enable --now mysql-failover即可。用systemd托管的好处是脚本万一崩了会自动拉起比裸nohup靠谱得多。5.4 实测中遇到的坑与排查技巧坑一mysqladmin ping返回成功但实际写不进去我遇到过主库磁盘写满的情况MySQL进程还活着mysqladmin ping也返回正常但实际上所有写操作都阻塞了。脚本误判为主库健康没有触发切换业务侧全部报错。这个问题的关键在于探活命令太轻量不能反映真实可用性。我的改进方式是在健康检查里增加一个“写探测”check_master_writable() { local test_value test_value$(mysql -h$MASTER_HOST -P$DB_PORT -u$DB_USER -p$DB_PASS \ -e CREATE DATABASE IF NOT EXISTS health_check; INSERT INTO health_check.probe(t) VALUES (NOW()); DELETE FROM health_check.probe WHERE t NOW() - INTERVAL 1 MINUTE; 2/dev/null) [ $? -eq 0 ] return 0 || return 1 }在检测函数里先探测存活再探测可写两个都通过才算健康。实测下来这个改动可以提前发现磁盘满、锁等待等“假活”情况。坑二MySQL 8.0默认认证插件导致脚本连不上前面提到了caching_sha2_password在某些老版本客户端下会连接失败。第一次部署脚本时我用的是系统自带的mariadb客户端连主库报认证错误。后来统一装了mysql官方8.0客户端问题解决。如果你不想动客户端可以在创建账号时指定老认证CREATE USER repl_monitor% IDENTIFIED WITH mysql_native_password BY StrongPass123!;不过MySQL 8.4开始已经移除mysql_native_password插件长期来看还是统一客户端版本更稳妥。坑三从库复制状态正常但实际已经中断Replica_IO_Running和Replica_SQL_Running显示Yes但Seconds_Behind_Source一直往上涨这种情况多半是主库产生了大量写入从库的SQL线程回放跟不上。脚本里如果只查两个线程状态会错过这个问题。我把落后阈值判断写进去之后至少能保证在从库落后超过30秒时不会触发提升避免把数据差距带到新主库。坑四脚本刚启动还没有状态时就触发了切换脚本第一次启动时FAIL_COUNT是0如果主库恰好此时不可用只需要3次检测就会触发切换。但这时候可能只是脚本所在的监控机网络有问题而不是主库真的挂了。所以我在脚本里加了一个“预热”机制启动后的前2分钟只记录状态不执行切换动作给监控机自己一个自检的机会。6. 常见问题速查与最终实操体会6.1 问题排查速查表我把实际运行中容易出现的问题整理成了一个表方便你对照排查现象可能原因排查命令或方法解决思路脚本不执行切换主库存活检测误判mysqladmin -h主库 ping手动执行看返回调整--connect_timeout检查监控账号权限脚本报“从库未就绪”从库复制中断或落后SHOW REPLICA STATUS\G看Last_IO_Error关注报错信息修复复制链路后再恢复监控VIP漂移后业务仍然不通ARP缓存未刷新在客户端arp -a看VIP对应MAC手动执行arping -c 3 -A -I eth0 VIP确认交换机上ARP表项已更新切到新主库后丢数据半同步未启用或退化为异步主库执行SHOW STATUS LIKE Rpl_semi_sync%确保半同步参数生效检查从库网络质量从库追不上主库大事务或慢查询拖累回放SHOW REPLICA STATUS看Seconds_Behind_Source优化大事务考虑开启并行复制参数回切后复制报主键冲突新主库写入了老主库曾经写过的数据检查Last_SQL_Error的报错位置先全量校验数据必要时用pt-table-checksum对齐后再挂复制6.2 脚本触发切换后的恢复流程自动切换成功之后不是万事大吉了后面的恢复同样重要。我团队现在的标准流程是确认告警通知业务侧主库已切换观察业务是否恢复正常登录日志逐条核对切换过程确认每一步的时间点和耗时检查旧主库机器查明故障根因磁盘、网络、硬件、误操作修复旧主库后按照前面mysql_failback.sh的流程把它挂回从库观察复制状态等落后秒数归零后再决定是否回切维护一次故障复盘文档记录时间线、根因、改进项这套流程建议至少每个季度演练一次别等到真故障了才第一次执行。我们团队现在每个季度挑一个低峰期人为模拟一次主库故障让脚本真正跑一遍切换再按流程恢复。演练过程中能发现很多平时想不到的问题比任何文档都有说服力。6.3 我在实际使用中的几点体会脚本上线到现在也有挺长时间了说几个我最有感触的点。第一自动切换脚本永远只是兜底不能替代日常巡检。真正让这套方案稳定运行的是平时对主从状态的持续关注。我习惯每天定时巡检一次Seconds_Behind_Source和半同步状态把潜在问题在爆发之前解决掉这样脚本才可能一直处于“没有机会用”的理想状态。第二告警别只发一种渠道。脚本的send_alert函数我接了企业微信和短信两个通道因为故障的时候可能恰好是凌晨没有哪个渠道是100%能送达的。多一层保障就少一次“脚本切了但没人知道”的尴尬。第三日志和状态文件是排查故障的第一手资料。我会定期把mysql_failover.log同步到独立的日志服务器上防止本机故障后日志一起丢失。复盘的时候这几行日志往往能直接还原当年的故障现场。第四也是最重要的一点脚本的每个开关、每个阈值都要有团队成员理解它的意义。我见过不少团队脚本是某一个人写的其他人只会用不会改。结果这个人一走脚本就成了一堆没人敢碰的代码。建议把配置项和核心流程做成文档在团队里做一次内部分享让至少两个人能讲清楚这套方案的工作原理。这样才真正称得上是一个团队的可靠性基础设施而不是某个人的私人工具。