半同步复制超时降级是设计行为而非故障,但需监控主库状态变量(如Rpl_semi_sync_master_off_times)和合理设置timeout(建议2000~5000ms),降级后依赖新事务触发恢复,且生效前提是有存活并完成握手的从库。

半同步复制超时退化为异步不是故障,而是设计行为;但如果不监控、不调参,它会静默失效,等出事才发现主库早就不等从库了。
为什么 SHOW SLAVE STATUS 看不出半同步已降级
因为降级只影响主库的提交逻辑,从库的 Slave_IO_Running 和 Seconds_Behind_Master 完全不受影响——它还在正常拉日志、写中继日志,只是主库不再等它的 ACK 了。真正反映状态的指标全在主库上:
-
Rpl_semi_sync_master_status:显示是否“当前启用”,但即使值为ON,也可能刚降级完还没恢复 -
Rpl_semi_sync_master_no_times:累计降级次数,持续上涨说明配置不合理 -
Rpl_semi_sync_master_off_times:因超时主动关闭半同步的次数,比no_times更关键 -
Rpl_semi_sync_master_wait_timeouts:超时等待发生的次数(注意不是“降级”次数,是单次等待失败数)
执行 SHOW STATUS LIKE 'Rpl_semi_sync%'; 才能确认真实运行态。光查 SHOW VARIABLES LIKE 'rpl_semi_sync%'; 只能看到配置,看不到运行结果。
rpl_semi_sync_master_timeout 设多少才合理
默认 10000 毫秒(10 秒)太保守,尤其在跨机房或高负载从库场景下,容易频繁触发降级;设成 100ms 又太激进,网络抖动就误判。实际应基于两点测算:
- 主从间平均网络 RTT(用
ping或mysqladmin ping跑多次取 P95) - 从库写 relay log + fsync 的耗时(可查
SHOW PROFILE FOR QUERY中 I/O 线程相关阶段)
建议初始值设为 2000 ~ 5000,上线后观察 Rpl_semi_sync_master_off_times 是否稳定增长。如果 24 小时内增长 > 3 次,说明该调高;如果几乎不涨但 Rpl_semi_sync_master_no_times 持续增加,说明从库响应慢,得查磁盘或 relay log 刷盘配置(如 sync_relay_log=1 是否开启)。
降级后不会自动恢复?得靠事务触发重试
半同步降级后,主库不会定时轮询从库状态,也不会在空闲时自动重试握手。恢复依赖下一个事务提交时重新发起等待流程——也就是说,如果主库长时间没写入,半同步就一直卡在“异步”状态。
- 不要依赖“等一会儿它自己好”,必须有主动触发机制
- 最稳妥的是在监控发现
Rpl_semi_sync_master_status = OFF时,执行一个轻量事务(比如INSERT INTO heartbeat VALUES (NOW());)来触发重试 - 或者定期
STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;(仅限从库端插件已启用且配置正确时)
特别注意:GTID 环境下,如果从库先启了 IO 线程再装插件,半同步握手永远错过,Rpl_semi_sync_master_status 会始终为 OFF,此时重启 IO 线程也无效,必须按顺序重做:装插件 → SET GLOBAL rpl_semi_sync_slave_enabled = ON; → START SLAVE IO_THREAD;。
超时阈值调低就能避免降级?别忽略 wait_for_slave_count
rpl_semi_sync_master_wait_for_slave_count 控制主库要等几个从库返回 ACK 才提交。默认是 1,但如果设成 2,而其中一台从库宕机或延迟高,哪怕另一台很快,主库仍会等到超时才降级。
- 生产环境建议保持为
1,除非你有明确的多副本强一致诉求 - 若需剔除不可用从库,应配合
rpl_semi_sync_master_wait_no_slave = ON(当可用半同步从库数不足时,不降级,而是继续等) - 但开启这个参数后,如果所有半同步从库都挂了,主库会无限阻塞——所以必须配好超时和告警,不能只调参不监控
真正容易被忽略的点是:半同步是否生效,不取决于你有没有开开关,而取决于“主库正在等的那一刻,有没有一个活着的、已握手成功的从库”。任何环节断链(插件名错、SO 文件路径错、GTID 启动顺序错、防火墙拦 ACK 包),都会让这个“等”变成空转,最后只能超时降级。











