半同步状态不自动恢复是设计行为,不是故障:rpl_semi_sync_master_status从off变回on需主库收到至少一个有效ack,不重试、不轮询,仅被动等待;超时降级后全走异步,直至真实收到ack才切换。

半同步状态不自动恢复是设计行为,不是故障
MySQL 半同步不会“自动恢复”——Rpl_semi_sync_master_status 从 OFF 变回 ON 的前提是主库**收到至少一个有效 ACK**。它不重试、不轮询、不定时检查,只被动等待。一旦超时降级,后续所有事务都走异步路径,直到下一次真正收到 ACK 才切换。这不是 bug,是机制:稳住时才坚持,扛不住就让路。
为什么 ACK 总是收不到:先盯住从库状态
主库 Rpl_semi_sync_master_status = OFF,不代表从库没开插件,而是它没发或主库没收到 ACK。必须逐个验证:
- 登录每个从库,确认
rpl_semi_sync_slave_enabled = ON且Rpl_semi_sync_slave_status = ON - 查主库
SHOW STATUS LIKE 'Rpl_semi_sync_master_clients':值为 0?说明没一个从库成功注册为半同步客户端 - 查从库
SHOW PROCESSLIST,看State是否卡在Waiting for master to send event或Reading event from the relay log—— 这往往意味着 relay log 写入慢或 SQL 线程积压 - 特别注意
Seconds_Behind_Master持续上涨:哪怕Slave_IO_Running = Yes,IO 线程拉到了日志,SQL 线程跟不上,ACK 就发不出(因为 ACK 是在 relay log 写入后由 slave 插件触发的)
rpl_semi_sync_master_timeout 设太小或太大都会卡死恢复
这个值不是越大越容易恢复,也不是越小越“强一致”。它必须落在真实 RTT 和负载波动的交集里:
- 局域网环境(RTT 稳定在 0.5–2ms):
rpl_semi_sync_master_timeout = 1000是合理起点,覆盖 99% 抖动;设成 100 或 500,UDP 小包丢包率稍高就秒退 - 跨机房或高负载场景(RTT 偶发 >15ms):
3000~5000可接受;但设到10000(默认值),单事务可能卡满 10 秒,业务已超时,反而更难恢复 - 改完立即生效:
SET GLOBAL rpl_semi_sync_master_timeout = 3000,无需重启 - 关键陷阱:只调 timeout 不查
Rpl_semi_sync_master_no_times和Rpl_semi_sync_master_off_times—— 如果这两项每小时涨几十次,说明不是参数问题,是网络或从库 IO 已经持续失能
底层系统配置常被忽略:TCP 接收队列决定 ACK 能不能活着到达
从库内核参数 net.ipv4.tcp_rmem 过小,会导致主库发来的 ACK 请求包被内核直接丢弃或严重延迟,即使网络 ping 得通、复制也正常,半同步照样失效:
- 检查命令:
sysctl net.ipv4.tcp_rmem,典型值应类似4096 65536 8388608;若中间值65536太小(如只有 16384),ACK 包极易积压丢弃 - 临时调大:
sysctl -w net.ipv4.tcp_rmem="4096 262144 8388608" - 同时检查从库是否混跑备份、大查询、或
slave_parallel_workers > 0但slave_parallel_type = DATABASE—— 这种组合下,SQL 线程还没执行完,IO 线程就提前发了 ACK,主库误以为已落盘,实际没写,后续状态校验失败,ACK 不被信任











