after_sync更安全:主库binlog刷盘后等待从库ack再提交事务,宕机可回滚;after_commit先提交再等ack,主库宕机易丢数据。mysql 5.7+默认after_sync。

因为主库必须卡住等待至少一个从库返回 ACK,而这个 ACK 只表示 relay log 缓冲区写入成功,不保证落盘或执行完成。
半同步的等待点在哪?AFTER_SYNC 和 AFTER_COMMIT 差别很大
MySQL 5.7 默认使用 rpl_semi_sync_master_wait_point = AFTER_SYNC:主库先刷 binlog 到磁盘,再等从库 ACK,最后才提交本地事务。这个顺序最安全——主库 crash 后,已确认的事务 binlog 一定已落盘,不会丢。
但代价是显性的:每条写事务都多了一次网络往返 + 从库缓冲区写入耗时。如果设成 AFTER_COMMIT,主库先提交、再发 binlog 并等 ACK,看起来快一点,但主库 crash 后可能丢失已“确认”但未同步成功的事务,且容易和 sync_binlog=1 冲突,触发双刷盘。
所以别为了“快一点”去切 AFTER_COMMIT,除非你明确接受一致性风险。
为什么一开 slave_compressed_protocol=ON 就卡 30 秒?
这是 MySQL 5.7.19 及更早版本的真实 Bug:半同步模块无法解析压缩协议下的 ACK 包,导致主库收不到有效响应,只能干等超时。那个“30 秒”,实际是 master_heartbeat_period 的默认值(30000 毫秒),不是 rpl_semi_sync_master_timeout。
验证方式很简单:
- 查
SHOW VARIABLES LIKE 'slave_compressed_protocol';—— 如果是ON,先关掉 - 查
SHOW VARIABLES LIKE 'master_heartbeat_period';—— 如果非零,说明心跳机制正在兜底 - 看错误日志有没有
Read semi-sync reply magic number error
修复方案只有三个:关压缩、升版本(≥5.7.21 或 ≥8.0.4)、或者两者都做。
rpl_semi_sync_master_wait_for_slave_count 设成 2 是自毁行为
设成 2 意味着主库必须等两个从库都返回 ACK 才能提交。但 ACK 不是“执行完”,只是“收到并缓存”。只要其中任意一个从库稍慢(比如 IO 延迟高、TCP 接收队列满、甚至临时 GC),整个写入就卡在它身上。
真实生产环境里,用 1 是合理底线;加更多节点不是提高可靠性,是放大延迟概率。监控 Rpl_semi_sync_master_no_times 和 Rpl_semi_sync_master_off_times 这两个状态变量,如果它们频繁增长,说明不是参数问题,而是从库真有瓶颈——该查 SHOW PROCESSLIST 里 Slave_IO_State 是否卡在 Waiting for master to send event 或 Queueing master event to the relay log 了。
半同步不是开个开关就完事的配置,它的性能拐点藏在网络 RTT、从库 IO 能力、以及主库自身刷盘策略的叠加里。最容易被忽略的是:你调了半天 rpl_semi_sync_master_timeout,结果真正卡住你的,是一条没关的 slave_compressed_protocol,或者一个误设的 wait_for_slave_count=2。











