在默认的异步复制架构下,MySQL主库提交事务后立即返回给客户端,完全不等待从库接收binlog。一旦主库在从库接收到日志之前宕机,未被传输的事务就会永久丢失。半同步复制(Semi-Synchronous Replication)正是为了解决这个问题而生:它要求主库在提交事务时,至少等待一个从库确认收到该事务的binlog,才向客户端返回提交成功。本文将从原理、模式差异、配置方法以及常见问题几个方面,系统讲解这套机制。

一、异步复制的缺陷与半同步的诞生背景
要理解半同步复制的价值,必须先看清异步复制的风险边界。在异步模式下,主库将事务写入binlog后,dump线程会立即将日志发送给从库,但主库的提交动作与从库的接收动作之间没有任何同步约束。假设主库在事务提交完成后、从库尚未收到binlog时发生崩溃,切换到从库后,这个事务就彻底丢失了。对于金融、订单等强一致性场景,这种丢失是不可接受的。
全同步复制理论上可以彻底避免丢失,即主库等待所有从库真正执行完事务并返回确认后才提交。但这种方式对网络和从库性能要求极高,写入延迟会被成倍放大,实际生产中几乎无人使用。半同步复制取了一个折中点:从库只需要确认接收到binlog(而不是执行完成),主库即可提交。这既大幅缩小了数据丢失窗口,又把性能损耗控制在可接受范围内。
需要注意,半同步保证的是“日志已到达从库”,而非“从库已应用日志”。如果主库提交后立即宕机,而从库虽然收到了日志但还没执行,切换时仍需依靠日志补齐,这正是半同步与全同步的本质区别。
二、半同步复制的核心工作流程
半同步复制通过插件方式实现,主库侧和从库侧各加载一个插件。其完整交互流程如下:
- 主库提交事务时,事务事件被写入binlog,同时主库的Ack Receiver线程开始等待从库的ACK确认;
- 从库的IO线程接收到binlog事件后,立即写入本地的relay log,并回报一个ACK给主库;
- 主库在收到ACK后,才正式完成提交并向客户端返回成功;
- 如果在超时时间(rpl_semi_sync_master_timeout)内没有收到任何ACK,主库会自动降级为异步复制,避免阻塞业务。
整个过程的关键在于ACK只代表“写入relay log完成”,与SQL线程的回放进度无关。从库写relay log的速度通常很快,因此半同步带来的延迟一般只有零点几毫秒到几毫秒,远小于全同步。
主库上有一个核心状态变量Rpl_semi_sync_master_status用于观察半同步是否生效,还有Rpl_semi_sync_master_tx_avg_wait_time可以查看每个事务的平均等待时间,是排查性能问题的重要指标。
三、AFTER_SYNC与AFTER_COMMIT两种模式对比
MySQL 5.7引入了rpl_semi_sync_master_wait_point参数,用于控制主库在事务提交的哪个阶段等待ACK,由此产生了两种模式:
| 模式 | 等待时机 | 幻读风险 | 特点 |
|---|---|---|---|
| AFTER_SYNC | 引擎提交前,binlog刷盘并同步到从库后 | 无 | 增强版半同步,推荐使用 |
| AFTER_COMMIT | 引擎提交后,等待ACK期间事务已可见 | 有 | 经典半同步,与5.6行为一致 |
AFTER_COMMIT模式下,事务在主库上已经完成引擎层提交,其他会话可以看到该事务的数据,但主库还在等从库ACK。如果此时主库宕机且切换到从库,客户端可能在主库上读到了数据,切换后又“读不到”了,这就是经典的幻读问题。AFTER_SYNC把等待点提前到存储引擎提交之前,事务在收到ACK前对其他会话不可见,从根本上消除了这一风险,因此生产环境建议统一使用AFTER_SYNC。
四、插件安装与参数配置实战
半同步复制基于插件,需要分别在主从库上安装。以MySQL 8.0为例,主库执行:
-- 主库安装半同步插件 INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so'; -- 开启半同步并设置等待点 SET GLOBAL rpl_semi_sync_master_enabled = 1; SET GLOBAL rpl_semi_sync_master_timeout = 3000; SET GLOBAL rpl_semi_sync_master_wait_point = 'AFTER_SYNC'; -- 至少等待几个从库ACK,默认1 SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 1;
从库侧对应的配置如下:
-- 从库安装半同步插件 INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so'; SET GLOBAL rpl_semi_sync_slave_enabled = 1; -- 重启IO线程使插件生效 STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
有几点容易被忽视:第一,从库修改参数后必须重启IO线程,否则半同步不会真正启用;第二,这些SET GLOBAL是内存级的,重启后失效,务必写入my.cnf的[mysqld]段持久化;第三,rpl_semi_sync_master_wait_for_slave_count可以设置等待多个从库,一主两从的架构设为2可获得更强的安全性,但延迟也会相应增加。
配置完成后,通过SHOW STATUS LIKE 'Rpl_semi_sync_master_status'检查状态,返回ON说明半同步正常工作。
五、常见踩坑点与排查思路
坑一:半同步静默退化为异步。当主库在超时时间内没收到ACK,会自动降级为异步复制,且不会报错。此时数据安全性已经悄悄下降,而业务毫无感知。排查方法是持续监控Rpl_semi_sync_master_status,一旦变为OFF立即告警。也可以故意把timeout设得极大(比如100年)来禁用自动降级,但这属于激进做法,网络抖动时会直接阻塞写入,需要权衡。
坑二:从库IO线程重启后半同步失效。插件加载发生在IO线程启动时,如果先安装插件再启动复制,或者顺序颠倒,都可能导致半同步没生效。标准做法是先INSTALL PLUGIN并设置参数,再START SLAVE。
坑三:误以为半同步等于零丢失。半同步只保证binlog送达至少一个从库,如果集群只有一个从库且该从库损坏,或使用了wait_for_slave_count为1但存在多个从库,未收到日志的从库仍可能丢失数据。切换主库前,务必用GTID核对候选从库的事务集合是否完整。
综合来看,半同步复制以极小的性能代价换来了数据丢失窗口的大幅收缩,是绝大多数MySQL高可用架构(如MHA、Orchestrer配合使用)的基础组件。掌握AFTER_SYNC模式的原理、做好降级监控,再结合GTID进行切换校验,就能构建出兼顾性能与可靠性的复制体系。
MySQL半同步复制主从复制replication修改时间:2026-09-01 02:12:30