MySQL 5.7半同步让主库卡在commit前阻塞等待至少一个从库ACK,流程为写binlog→刷盘→发送→等ACK→提交,该等待是同步阻塞而非异步,网络抖动或从库延迟均直接拖慢事务响应。

半同步让主库卡在 commit 前等 ACK
MySQL 5.7 默认的 rpl_semi_sync_master_wait_point = AFTER_SYNC,主库事务流程是:写 binlog → sync_binlog=1 时刷盘 → 发送给从库 → 阻塞等待至少一个从库 ACK → 才提交事务。这个“等 ACK”不是后台异步动作,而是事务线程原地挂起,客户端必须等到这一步完成才能收到响应。
常见错误现象:QPS 没变、CPU 不高,但 innodb_row_lock_time_avg 或慢查里大量出现 Waiting for semi-sync ACK from slave;SHOW PROCESSLIST 里能看到大量 commit 状态的线程卡住。
- 哪怕从库平均 ACK 耗时仅 20ms,P99 达到 80ms,主库写入延迟就会被拉高到接近该值
- 网络抖动、从库
slave_pending_jobs_size_max溢出、TCP window 满了,都会让等待时间陡增 -
rpl_semi_sync_master_timeout单位是毫秒,设成100容易频繁降级;设成10000又会让单条事务多等 10 秒
AFTER_COMMIT 模式能降低延迟但牺牲强一致性
把 rpl_semi_sync_master_wait_point 改成 AFTER_COMMIT,主库流程变成:写 binlog → 刷盘 → 发送 → 提交事务 → 再等 ACK。客户端收到响应更快,但代价明确:主库 crash 后,已提交但未收到 ACK 的事务可能丢失。
使用场景:读多写少、允许极小概率数据不一致的业务(如日志类、统计类);不能用于支付、订单等要求“已提交即落地”的场景。
- 此时
Rpl_semi_sync_master_status仍为 ON,但Rpl_semi_sync_master_yes_tx和Rpl_semi_sync_master_no_tx的比值会显著升高(说明更多事务走的是“先提交后等”路径) - 别只看
SHOW VARIABLES LIKE 'rpl_semi_sync%'全是 ON 就以为安全——那只是插件开了,不是语义上“已同步” - 验证是否真生效:抓包看主库
binlog dump线程发完 event 后,是否真等到从库 TCP ACK + 协议层 semi-sync response 才继续
哪些配置组合会让延迟爆炸式增长
不是所有“加强可靠性”的参数叠加都合理,有些组合本质是性能黑洞:
-
sync_binlog=1+innodb_flush_log_at_trx_commit=1+ 半同步:三次强制刷盘(binlog、redo、relay log)+ 一次网络等待,单事务延迟轻松突破 10ms,尤其在 SSD 高负载时 -
rpl_semi_sync_master_wait_for_slave_count=2:要求两个从库都 ACK,不是高可用,是自设瓶颈;只要其中一个慢,整体就卡住 - 从库开了
slave_parallel_workers > 0但没配slave_parallel_type = 'LOGICAL_CLOCK':ACK 发送时机不可控,SQL 线程还没执行完,I/O 线程就返回了 relay log 接收确认,主库误判“已就绪”
怎么验证 ACK 是真有效,不是假阳性
线上开了半同步,不代表它真在起作用。很多故障源于“开关开着,但实际已退化为异步”:
- 查状态变量:
Rpl_semi_sync_master_status是 ON 表示当前处于半同步模式;Rpl_semi_sync_master_no_times和Rpl_semi_sync_master_off_times如果持续增长,说明频繁超时退化,得立刻查从库SHOW PROCESSLIST里 I/O 线程是否卡住 -
Seconds_Behind_Master = 0不代表数据已落地——大事务下 SQL 线程还在执行中,但主库早已返回成功 - 用
pt-heartbeat测真实业务延迟:它写心跳记录并反向计算从库滞后时间,比Seconds_Behind_Master更贴近业务感知
真正影响延迟的从来不是“有没有半同步”,而是“等什么、等多久、等谁”。参数调得太保守,主库寸步难行;调得太松,又等于没开。关键是要根据你的网络 RTT 分布、从库负载水位、业务容忍度,去压测 P95/P99 的 ACK 耗时,再反推 timeout 和 wait_point 的取值。


















