commit卡住主因是after_sync模式下主库阻塞于binlog刷盘后等待从库ack,任意环节(网络、从库io、tcp队列)卡顿均导致事务无法提交。

事务提交卡住,大概率是半同步在等 ACK 时超时未触发降级,或从库根本没响应。
为什么 COMMIT 会挂住不返回
MySQL 5.7 默认用 rpl_semi_sync_master_wait_point = AFTER_SYNC,主库必须做完三件事才允许事务真正提交:写 binlog、刷盘(fsync)、收到至少一个从库的 ACK。任意一环卡住,COMMIT 就停在那——不是慢,是阻塞。
- 常见现象:
SHOW PROCESSLIST里看到大量线程状态为Waiting for semi-sync ACK from slave - 真实瓶颈往往不在网络带宽,而在从库 TCP 接收队列积压、
relay_log刷盘慢(sync_relay_log = 0时更隐蔽),或从库 SQL 线程已严重延迟但 IO 线程还在假性“健康” - 别只查
Rpl_semi_sync_master_status是否为ON:它只表示插件启用,不反映实时等待是否卡死;要看Rpl_semi_sync_master_tx_waits是否持续增长,以及Rpl_semi_sync_master_no_tx是否飙升
立刻缓解:强制降级 + 定位根因
挂住时优先保业务可用,再追因:
- 临时关闭半同步(主库执行):
SET GLOBAL rpl_semi_sync_master_enabled = 0;,事务立即恢复异步行为 - 检查从库是否真在线:
SHOW SLAVE STATUS\G,确认Slave_IO_Running和Slave_SQL_Running都是Yes,且Seconds_Behind_Master接近 0 - 查从库负载:
SHOW PROCESSLIST看是否有长事务或锁等待;iostat -x 1观察%util和await是否异常高 - 确认从库 relay log 刷盘策略:
SELECT @@sync_relay_log;,若为0,IO 线程写完就发 ACK,但数据可能还在 OS 缓存里——主库以为 OK,实际没落盘,建议设为1
rpl_semi_sync_master_timeout 怎么设才不误事
这个值不是“越小越快”,而是要匹配你网络的实际 RTT 波动峰:
- 内网环境(如同城机房):
SET GLOBAL rpl_semi_sync_master_timeout = 1000;(1 秒)足够覆盖抖动,超时后秒切异步,用户无感 - 跨机房(如异地双活):
5000起步,但必须配合监控Rpl_semi_sync_master_no_times;如果它每小时涨几十次,说明超时太紧,得调大 - 绝对不要设为
0:等于禁用超时机制,主库会无限等下去 - 也别信某些文档写的“设成 10000 毫秒最稳”——10 秒超时,应用层早超时了,等来的不是可靠性,是雪崩
哪些配置组合会让半同步“表面正常、实际废掉”
最容易被忽略的是参数间的隐性冲突:
-
slave_parallel_workers > 0但slave_parallel_type != 'LOGICAL_CLOCK':并行回放未生效,SQL 线程还是单点瓶颈,IO 线程却照常发 ACK,主库误判从库“跟得上” - 主库开了
sync_binlog = 1+innodb_flush_log_at_trx_commit = 1,又配AFTER_SYNC:三次强制刷盘(binlog、redo、relay log),单事务延迟轻松破 10ms,高并发下Rpl_semi_sync_master_tx_waits直线上升 - 从库用了
replicate-ignore-db或binlog-do-db:GTID 模式下这类库级过滤已被弃用,会导致 ACK 发送失败或状态错乱,Rpl_semi_sync_master_status可能静默变OFF
半同步不是开个开关就能防丢数据的保险丝,它是一条依赖链:主库刷盘 → 网络送达 → 从库接收 → 从库刷 relay log → 从库发 ACK。任一环节没对齐配置或负载,整条链就卡在“等”字上。最危险的不是它挂住,而是你只盯着 SHOW STATUS 里的 ON,却没看 Rpl_semi_sync_master_no_tx 已经悄悄涨了上千次。











