半同步退化是设计行为而非故障,主库超时后自动降级为异步以保障可用性,但需通过Rpl_semi_sync_master_status等状态变量监控真实运行态,结合合理timeout设置(2000~5000ms)与主动事务触发恢复机制。

半同步退化是设计行为,不是故障
MySQL 5.7 的半同步复制在超时后自动退化为异步,这是明确的容错机制,不是 bug 或配置错误。它的存在意义是避免主库因从库异常而无限阻塞——比如从库宕机、网络中断、relay log 写盘卡住时,主库仍能继续提供写服务。
关键点在于:退化本身不可怕,可怕的是你不知道它已经退了。因为 SHOW SLAVE STATUS 完全看不出状态变化,从库照样在拉日志、Seconds_Behind_Master 看起来一切正常,但主库早已不等 ACK。
真正反映退化与否的指标全在主库上:
-
Rpl_semi_sync_master_status:值为OFF就表示当前已退为异步 -
Rpl_semi_sync_master_off_times:记录因超时主动关闭半同步的次数,持续上涨说明配置或从库有问题 -
Rpl_semi_sync_master_wait_timeouts:单次等待失败计数,注意它不等于降级次数(一次事务可能重试多次)
rpl_semi_sync_master_timeout 设太小就容易误降级
默认值 10000(10 秒)在跨机房或高负载场景下过于保守,频繁触发退化;设成 100 又太激进,普通网络抖动就会导致降级。合理值应基于实测:
- 用
ping或mysqladmin ping测主从 P95 RTT - 查从库
SHOW PROFILE FOR QUERY中 I/O 线程写 relay log + fsync 耗时 - 初始建议设为
2000~5000,上线后观察Rpl_semi_sync_master_off_times - 若 24 小时内增长 > 3 次,说明 timeout 偏小;若几乎不涨但
Rpl_semi_sync_master_no_times持续增加,说明从库响应慢,得查sync_relay_log=1是否启用、磁盘 I/O 是否瓶颈
降级后不会自动恢复,必须靠事务触发
半同步降级后,主库不会轮询、不会定时重试,更不会“等一会儿自己好”。恢复完全依赖下一个事务提交时重新发起等待流程——也就是说,如果主库长时间没写入,它就一直卡在异步状态。
所以不能只靠监控告警,还得有主动触发机制:
- 监控发现
Rpl_semi_sync_master_status = OFF时,执行轻量事务,例如:INSERT INTO heartbeat VALUES (NOW()); - 或者定期在从库执行:
STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;(前提是插件已启用且relay_log非空)
AFTER_SYNC 模式下退化不影响数据一致性保障
只要客户端收到了 COMMIT 成功响应,该事务就一定已落盘到至少一个从库的 relay log 中——这是 AFTER_SYNC 模式的硬保证,也是 MySQL 5.7+ 默认行为。
退化只影响后续新事务:一旦降级,新事务不再等待 ACK,但已成功返回的事务不受影响。所以问题不在于“会不会丢数据”,而在于“你是否知道现在写的每一条都可能丢了”。
最容易被忽略的点是:SHOW VARIABLES LIKE 'rpl_semi_sync%' 只显示配置,SHOW STATUS LIKE 'Rpl_semi_sync%' 才反映真实运行态。不查 status,等于没监控。


















