半同步退化是超时机制的必然结果,非故障:主库提交事务后等待从库ack,超rpl_semi_sync_master_timeout即降级为异步;降级后不自动恢复,需新事务触发重试。

半同步退化不是故障,是超时机制的必然结果
MySQL 半同步复制在网络抖动时退化为异步,根本原因就一条:rpl_semi_sync_master_timeout 到了没收到 ACK。它不是 bug,也不是配置漏了,而是半同步设计里写死的保底逻辑:等不到就切,不卡死主库。
主库提交事务后,会阻塞并等待至少一个从库返回 ACK;只要在 rpl_semi_sync_master_timeout 毫秒内没等到,就立刻降级为异步,继续处理后续请求。这个“降级”动作本身很快,用户几乎感知不到延迟突增,但数据一致性保障已悄然失效。
ping 延迟低 ≠ ACK 不会超时
很多人查 ping 主从间延迟只有 0.5ms,就以为 rpl_semi_sync_master_timeout 设成 100ms 绰绰有余——这是最典型的误判。
- ACK 是 TCP 小包,走的是从库的
slave_io_thread→ 内核 TCP 栈 → 网卡 → 网络设备 → 主库网卡 → 内核接收队列 → MySQL 主库线程,任意一环积压都会拖慢 ACK -
ping测的是 ICMP 包往返,而 ACK 是应用层触发、内核发包、可能被限速或丢弃的 TCP segment - 用
iperf -c 从库IP -t 30 -u测 UDP 丢包率更贴近 ACK 行为;丢包 >0.1% 就足以让 100ms 超时频繁触发 - 检查从库
netstat -s | grep "packet reassemblies failed"或ss -i看接收队列是否常满(rcv_space接近rmem)
SHOW SLAVE STATUS 看不出半同步是否已退化
你在从库上执行 SHOW SLAVE STATUS,看到 Slave_IO_Running: Yes、Seconds_Behind_Master: 0,不代表主库还在半同步模式——它只说明从库拉日志正常,跟主库“等不等 ACK”完全无关。
真正反映半同步运行态的指标全在主库上,必须查:
-
SHOW STATUS LIKE 'Rpl_semi_sync_master_status';—— 当前是否启用(注意:刚降级完可能还显示ON,但已不生效) -
SHOW STATUS LIKE 'Rpl_semi_sync_master_off_times';—— 因超时主动关闭半同步的次数,持续上涨=网络或从库 IO 不稳 -
SHOW STATUS LIKE 'Rpl_semi_sync_master_wait_timeouts';—— 单次等待失败次数(不是降级次数),飙升说明 ACK 频繁迟到
别只看 SHOW VARIABLES LIKE 'rpl_semi_sync%';,那只是配置值,不是运行态。
降级后不会自动恢复,得靠新事务触发重试
半同步降级后,主库不会轮询、不会心跳、不会定时重连——它安静地等下一个事务来,再发起一次等待流程。如果主库刚好进入空闲期,Rpl_semi_sync_master_status 就会一直卡在 OFF,直到有写入发生。
所以监控发现状态为 OFF 时,不能等,要主动干预:
- 执行一个轻量事务:
INSERT INTO heartbeat (ts) VALUES (NOW());,触发重试 - 或者在从库端确认
Rpl_semi_sync_slave_status = ON后,执行STOP SLAVE IO_THREAD; START SLAVE IO_THREAD; - 避免用
SET GLOBAL rpl_semi_sync_master_enabled = 0; SET GLOBAL rpl_semi_sync_master_enabled = 1;,这会清空等待队列,反而中断正在排队的事务
网络抖动本身难根治,但把 rpl_semi_sync_master_timeout 设在真实 RTT P95 + 从库 fsync 耗时的 2–3 倍区间(常见 2000–5000),再盯住 Rpl_semi_sync_master_off_times 的小时级增幅,才能让半同步真正“稳得住”,而不是反复开关灯。











