一句话总结
在 AFTER_SYNC 模式下,只要客户端收到"事务提交成功",这条数据就永远不会丢失------即使主库立刻宕机。这是 MySQL 5.7+ 默认半同步模式的核心价值。
背景:为什么需要半同步复制?
MySQL 默认使用异步复制 :主库写完 binlog 就返回成功,从库在后台慢慢拉取。
这种模式性能高,但存在致命缺陷:主库 crash 后,已"成功"的事务可能根本没传到从库,导致数据永久丢失。
为解决这个问题,MySQL 引入了半同步复制(Semi-Synchronous Replication):
主库必须等待至少一个从库确认收到 binlog 后,才向客户端返回成功。
但"什么时候等"?这就引出了两种关键模式:AFTER_COMMIT 和 AFTER_SYNC。
半同步模式的版本发展
| MySQL 版本 | 半同步复制状态 |
|---|---|
| 5.1.x(≥5.1.47) | 以插件形式提供(semisync_master.so / semisync_slave.so ),需手动加载 |
| 5.5(2010) | 官方 GA 支持,仍为插件,但文档和稳定性提升 |
| 5.6(2013) | 支持 AFTER_COMMIT 模式(默认) |
| 5.7(2015) | 新增 AFTER_SYNC 模式,并设为默认,大幅提升数据安全性 |
| 8.0(2018+) | 插件默认编译进二进制包,使用更便捷;与 GTID、Performance Schema 深度集成 |
AFTER_COMMIT 模式:看似安全,实则危险
这是 MySQL 5.6 的默认行为,流程如下:
- 主库执行事务;
- InnoDB 提交事务(数据对外可见);
- 写入 binlog;
- 发送给从库,等待 ACK;
- 收到 ACK 后,返回客户端成功。
致命缺陷
如果主库在第 2 步后、第 4 步前 crash:
- 客户端已经看到"提交成功";
- 但从库还没收到 binlog;
- 故障切换后,这条"成功"的数据消失了!
这就是典型的 "幻读 + 数据丢失" 场景,金融系统绝对无法接受。
AFTER_SYNC 模式:真正安全的提交顺序
MySQL 5.7+ 将默认模式改为 AFTER_SYNC,流程调整为:
- 主库执行事务;
- InnoDB prepare 事务(不提交);
- 写入完整 binlog(含 COMMIT 标记
**Xid_log_event**)并刷盘; - 发送 binlog 给从库,等待 ACK;
- 收到 ACK 后,才执行 InnoDB commit;
- 返回客户端成功。
为什么更安全?
关键在于:binlog 的持久化发生在 InnoDB commit 之前,且 ACK 是 commit 的前提。
这意味着:
- 只要从库 ACK 了,说明 binlog 已安全落盘;
- 即使主库在 commit 前 crash,重启后也会通过 崩溃恢复机制 自动提交该事务(因为 binlog 中有
Xid_log_event); - 从库后续 apply 时也会 commit;
- 最终主从都有数据,绝不丢失。
📌 核心保障 :
客户端看到"成功" = 事务已在主库 binlog 中持久化 + 至少一个从库已备份。
常见疑问澄清
❓ 从库会不会"提前"看到数据,而主库还没提交?
理论上可能,但不影响安全性。
- 从库 SQL Thread 可能在主库 commit 前就 apply 并 commit 了事务(因为它收到了完整的 binlog,含
Xid_log_event); - 此时数据在从库可见,但客户端尚未收到成功响应;
- 如果主库随后 crash,它会通过 binlog 自动恢复并 commit;
- 所以:从库看到的数据,主库迟早也会有 → 最终一致。
💡 注意:这不是 bug,而是异步 apply 的自然现象。若业务需要"写后读一致性",应读主库或使用 GTID 等机制等待同步。
❓ 从库 apply 时不检查主库是否 commit,会不会出错?
不会。因为:
- 从库 apply 的依据是 relay log 中的
Xid_log_event; - 而主库只有在 binlog 完整写入后才会发送它;
- MySQL 的 crash-safe 机制 (
sync_binlog=1+innodb_flush_log_at_trx_commit=1)确保:
binlog 里有的事务,InnoDB 最终一定 commit。
配置建议
plain
# my.cnf (主库)
[mysqld]
plugin-load-add = semisync_master.so
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_wait_point = AFTER_SYNC # 必须!
rpl_semi_sync_master_timeout = 5000000 # 5秒超时(单位微秒)
rpl_semi_sync_master_wait_for_slave_count = 1 # 至少1个从库ACK
# 从库
plugin-load-add = semisync_slave.so
rpl_semi_sync_slave_enabled = 1
🔒 务必配合:
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
否则 crash-safe 无法保证,半同步的安全性会大打折扣。
注意:半同步不是"全同步",且会自动降级
需要特别强调两点,避免误解:
1. MySQL 没有真正的"全同步复制"
- 半同步 ≠ 全同步 。
MySQL 的半同步只要求 binlog 被从库接收并落盘(relay log) ,不要求从库已 apply(执行)事务。 - 从库的数据可见性仍然是异步的,主从之间依然存在延迟。
- 如果你需要强一致读 (如"写后立即读从库必须看到"),半同步无法满足,需借助:
- 读主库
- 使用官方的 innodb cluster (MySQL Group Replication +MySQL Router +MySQL Shell) 的高可用方案, Group Replication(MGR 组复制) +
group_replication_consistency(读写一致性控制配置)实现强一致性读 - 应用层等待 GTID 同步(
WAIT_FOR_EXECUTED_GTID_SET) 大概方案是使用中间件比较&等待事务gtid 的 binlog 被应用再读取
📌 真正的"全同步复制"意味着主库必须等从库执行完事务并提交才返回成功------这会极大牺牲性能,且 MySQL 原生不支持。
2. 半同步会在超时后自动降级为异步复制
- 主库在等待从库 ACK 时,如果超过
rpl_semi_sync_master_timeout(默认 10 秒),
会自动切换回异步模式,不再等待,直接提交并返回成功。 - 目的是避免主库因网络抖动或从库故障而完全不可用。
- 降级期间写入的事务不再受半同步保护,存在丢失风险。
- 当从库恢复后,半同步会自动重新启用(前提是配置了自动开关)。
⚠️ 因此,半同步不能 100% 防丢数据 ,它是在"高可用"和"数据安全"之间做的权衡。
若业务要求"绝对不丢",还需结合:
- 多从库 ACK(
wait_for_slave_count > 1) - 监控半同步状态(
SHOW STATUS LIKE 'Rpl_semi_sync%') - 配合 MHA / Orchestrator 等高可用工具做安全 failover
最后一句话:
半同步复制不是银弹,但它是在性能可接受的前提下,MySQL 提供的最实用的数据持久性增强方案。
正确理解其原理、边界与降级行为,才能在架构设计中用好它。
作者 :一位在深夜和 MySQL 较真的工程师
延伸阅读:GTID 复制、InnoDB Cluster、Group Replication 一致性读、MySQL 崩溃恢复原理