个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <
文章目录
- 半同步复制:数据安全和服务可用之间怎么权衡
-
- 一、三种复制方式对比
- 二、半同步的原理
- 三、怎么配置
-
- [3.1 安装插件](#3.1 安装插件)
- [3.2 开启](#3.2 开启)
- [3.3 配置文件(永久)](#3.3 配置文件(永久))
- 四、关键参数
-
- [4.1 `rpl_semi_sync_master_timeout`](#4.1
rpl_semi_sync_master_timeout) - [4.2 `rpl_semi_sync_master_wait_no_slave`](#4.2
rpl_semi_sync_master_wait_no_slave) - [4.3 `rpl_semi_sync_master_wait_point`(MySQL 5.7+)](#4.3
rpl_semi_sync_master_wait_point(MySQL 5.7+)) -
- [AFTER_SYNC vs AFTER_COMMIT](#AFTER_SYNC vs AFTER_COMMIT)
- [4.1 `rpl_semi_sync_master_timeout`](#4.1
- 五、监控
- 六、半同步的代价
-
- [6.1 增加主库写延迟](#6.1 增加主库写延迟)
- [6.2 从库故障会影响主库](#6.2 从库故障会影响主库)
- [6.3 网络抖动敏感](#6.3 网络抖动敏感)
- [七、8.0 的改进](#七、8.0 的改进)
-
- [7.1 参数重命名](#7.1 参数重命名)
- [7.2 性能改进](#7.2 性能改进)
- 八、什么时候用
- [九、和 MGR 的关系](#九、和 MGR 的关系)
- [十、和 GTID 配合](#十、和 GTID 配合)
- 十一、故障处理
-
- [11.1 半同步一直降级](#11.1 半同步一直降级)
- [11.2 主库写入变慢](#11.2 主库写入变慢)
- 十二、配置模板
- 小结
半同步复制:数据安全和服务可用之间怎么权衡
异步复制丢数据,全同步太慢。
半同步是折中方案------但有它自己的坑。
一、三种复制方式对比
| 方式 | 主库提交时 | 数据安全性 | 性能 |
|---|---|---|---|
| 异步(默认) | 写完本地 binlog 就返回 | 主库挂了丢数据 | 最好 |
| 半同步 | 等至少一个从库确认收到 | 好 | 中等 |
| 全同步 | 等所有从库都应用完 | 最好 | 最差 |
异步复制丢失数据的场景
1. 主库执行完事务,写 binlog
2. 主库返回"成功"给客户端
3. binlog 还没传给从库
4. 主库宕机
5. 切主 → 这个事务丢了
已经返回成功了,数据却没了------这是最难受的情况。
二、半同步的原理
1. 主库执行事务,写 binlog
2. 主库把 binlog 发给从库
3. 从库收到并写入 relay log,给主库一个 ACK
4. 主库收到 ACK,才返回成功给客户端
注意 ACK 的时机 :从库收到 binlog 并落 relay log 就 ACK,
不需要应用完。
所以从库可能"收到了但还没执行"------
中间还有一小段风险窗口,但比异步小得多。
三、怎么配置
3.1 安装插件
主库:
sql
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
从库:
sql
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
8.0 的参数名改了(更中性):
sql
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so'; -- 主
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so'; -- 从
老的参数名在 8.0 还能用(会废弃警告)。
3.2 开启
sql
-- 主库
SET GLOBAL rpl_semi_sync_master_enabled = ON;
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- 毫秒
-- 从库
SET GLOBAL rpl_semi_sync_slave_enabled = ON;
从库要重启 IO 线程才生效:
sql
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
3.3 配置文件(永久)
ini
# 主库
rpl_semi_sync_master_enabled = ON
rpl_semi_sync_master_timeout = 1000
rpl_semi_sync_master_wait_for_slave_count = 1
# 从库
rpl_semi_sync_slave_enabled = ON
四、关键参数
4.1 rpl_semi_sync_master_timeout
最重要的参数。
sql
SET GLOBAL rpl_semi_sync_master_timeout = 1000; -- 1 秒
意思是:等从库 ACK 最多等 1 秒,
超时了就降级为异步复制,正常返回给客户端。
这是"半"同步的关键 ------
它不会无限等待,保证可用性优先。
sql
SHOW STATUS LIKE 'Rpl_semi_sync_master_status';
-- ON = 半同步生效中
-- OFF = 已降级为异步
4.2 rpl_semi_sync_master_wait_no_slave
sql
SET GLOBAL rpl_semi_sync_master_wait_no_slave = ON; -- 默认
没有从库连接时怎么办:
| 值 | 行为 |
|---|---|
| ON(默认) | 没有从库时继续等待,直到超时降级 |
| OFF | 没有从库时立刻降级为异步 |
一般保持默认。
4.3 rpl_semi_sync_master_wait_point(MySQL 5.7+)
控制 ACK 的时机:
sql
SET GLOBAL rpl_semi_sync_master_wait_point = AFTER_SYNC; -- 默认,推荐
| 值 | 时机 | 特点 |
|---|---|---|
AFTER_SYNC |
写完 binlog 后、提交前等待 | 无损,推荐 |
AFTER_COMMIT |
提交后等待 | 有"幻读"问题 |
AFTER_SYNC vs AFTER_COMMIT
AFTER_COMMIT 的问题:
1. 主库提交事务(引擎层已提交)
2. 等待从库 ACK
3. 此时其他连接已经能看到这个事务的结果
4. 主库宕机,从库没收到 → 切主后数据没了
5. 但之前读到这个数据的应用以为成功了
这叫"phantom read in failover"。
AFTER_SYNC 的做法:
1. 主库写 binlog
2. 等从库 ACK
3. 收到 ACK 后才提交到引擎层
4. 返回成功
引擎层提交之前就保证了从库已收到 ,
切换后不会丢。
MySQL 5.7 起默认 AFTER_SYNC,这是无损半同步。
五、监控
sql
SHOW STATUS LIKE 'Rpl_semi_sync%';
主库关键指标:
| 指标 | 含义 |
|---|---|
Rpl_semi_sync_master_status |
ON/OFF,是否降级 |
Rpl_semi_sync_master_clients |
有几个半同步从库 |
Rpl_semi_sync_master_tx_avg_wait_time |
平均等待 ACK 时间 |
Rpl_semi_sync_master_no_tx |
退化为异步的事务数 ← 重点 |
Rpl_semi_sync_master_yes_tx |
半同步成功的事务数 |
Rpl_semi_sync_master_timeouts |
超时次数 |
告警项:
Rpl_semi_sync_master_status = OFF→ 已降级Rpl_semi_sync_master_timeouts增长 → 网络或从库有问题
六、半同步的代价
6.1 增加主库写延迟
每个事务多一个网络往返。
| 网络 | 额外延迟 |
|---|---|
| 同机房 | 0.2~1 ms |
| 同城跨机房 | 1~3 ms |
| 跨城 | 10~30 ms |
同机房基本无感,跨城就有影响了。
6.2 从库故障会影响主库
从库挂了 → 收不到 ACK → 每个事务都要等 timeout(比如 1 秒)
→ 主库写性能暴跌。
这是半同步最大的风险点。
缓解:
- timeout 设小一点(比如 500ms~1s)
- 监控
Rpl_semi_sync_master_status,降级时立刻告警 - 至少配 2 个半同步从库
6.3 网络抖动敏感
网络一抖,主库写入就卡。
生产建议 :主库和至少一个半同步从库放在同机房 ,
跨机房的从库用异步。
七、8.0 的改进
7.1 参数重命名
rpl_semi_sync_master_* → rpl_semi_sync_source_*
rpl_semi_sync_slave_* → rpl_semi_sync_replica_*
SHOW SLAVE STATUS → SHOW REPLICA STATUS
老名字还能用(有 deprecation 警告)。
7.2 性能改进
8.0 对半同步的等待机制做了优化,
高并发下性能比 5.7 好不少。
5.7 有个著名的 bug:
半同步在高并发下因为互斥锁导致性能下降严重。8.0 修复了。
八、什么时候用
| 场景 | 建议 |
|---|---|
| 金融、支付、订单 | 用半同步 |
| 一般业务,能容忍秒级数据丢失 | 异步就够 |
| 跨地域容灾 | 同机房半同步 + 异地异步 |
| 从库只是做报表/备份 | 异步(别让它拖慢主库) |
典型架构:
主库(机房 A)
├─ 半同步 ─→ 从库 1(机房 A) ← 高可用切换目标
├─ 异步 ─→ 从库 2(机房 B) ← 异地容灾
└─ 异步 ─→ 从库 3(机房 B) ← 报表查询
只让高可用切换的目标用半同步,别的所有从库都用异步。
九、和 MGR 的关系
MySQL Group Replication(MGR)内部用的就是类半同步机制,
但它是一套完整的组复制方案:
| 维度 | 半同步复制 | MGR |
|---|---|---|
| 故障切换 | 要外部工具(MHA/Orchestrator) | 内置自动切换 |
| 一致性协议 | 简单的 ACK | Paxos 变体 |
| 写冲突检测 | 无 | 有 |
| 多主 | 不支持 | 支持(但少用) |
| 部署复杂度 | 低 | 中 |
如果只需要"主库写不丢",半同步够用且简单。
如果要自动故障切换,考虑 MGR。
十、和 GTID 配合
ini
gtid_mode = ON
enforce_gtid_consistency = ON
有了 GTID,故障切换时能准确判断哪个从库的数据最新。
sql
SELECT @@gtid_executed;
-- 对比各实例的 GTID 集合
强烈建议开启 GTID,它让切换变得可靠得多。
十一、故障处理
11.1 半同步一直降级
sql
SHOW STATUS LIKE 'Rpl_semi_sync_master_status'; -- OFF
排查:
sql
-- 1. 有几个从库连着
SHOW STATUS LIKE 'Rpl_semi_sync_master_clients';
-- 2. 从库的 IO 线程状态
SHOW SLAVE STATUS\G
-- Slave_IO_Running: Yes?
-- 3. 从库有没有开半同步
SELECT @@rpl_semi_sync_slave_enabled;
-- 4. 网络延迟
ping / mtr 检查
11.2 主库写入变慢
sql
SHOW STATUS LIKE 'Rpl_semi_sync_master_tx_avg_wait_time';
-- 这个值很大说明从库响应慢
可能原因:
- 从库磁盘 IO 打满(写 relay log 慢)
- 网络延迟大
- 从库负载高
应急:先降级为异步恢复主库性能。
sql
SET GLOBAL rpl_semi_sync_master_enabled = OFF;
十二、配置模板
ini
# ===== 主库 =====
[mysqld]
server_id = 1
log_bin = mysql-bin
gtid_mode = ON
enforce_gtid_consistency = ON
plugin_load = "rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled = ON
rpl_semi_sync_master_timeout = 1000
rpl_semi_sync_master_wait_point = AFTER_SYNC
rpl_semi_sync_master_wait_for_slave_count = 1
# ===== 从库(半同步的那一个)=====
[mysqld]
server_id = 2
relay_log = relay-bin
read_only = ON
plugin_load = "rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled = ON
小结
| 维度 | 说明 |
|---|---|
| 解决什么 | 异步复制主库宕机丢数据 |
| ACK 时机 | 从库收到 binlog 并落 relay log |
| 关键参数 | rpl_semi_sync_master_timeout |
| 等待点 | 用 AFTER_SYNC(无损),不用 AFTER_COMMIT |
| 代价 | 每个事务多一个网络往返 |
| 最大风险 | 从库故障时主库写入卡顿 |
| 监控重点 | Rpl_semi_sync_master_status、_timeouts |
三条实用建议:
- 只对高可用切换的目标从库开半同步,其他从库用异步
- 半同步从库和主库放同机房,避免跨机房抖动影响主库写入
wait_point用 AFTER_SYNC (MySQL 5.7+ 默认就是这个),
别手动改成 AFTER_COMMIT
什么时候不用:
- 数据丢了能重放(比如可以从消息队列重建)
- 从库经常故障/网络不稳
- 对写入延迟极度敏感且数据能容忍丢失
半同步是个"用一点性能换数据安全"的取舍,
在支付、订单这类场景很值得 ;
在日志、埋点这类场景没必要。