半同步复制:数据安全和服务可用之间怎么权衡

个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <

文章目录

半同步复制:数据安全和服务可用之间怎么权衡

异步复制丢数据,全同步太慢。

半同步是折中方案------但有它自己的坑。

一、三种复制方式对比

方式 主库提交时 数据安全性 性能
异步(默认) 写完本地 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 秒)

→ 主库写性能暴跌。

这是半同步最大的风险点。

缓解:

  1. timeout 设小一点(比如 500ms~1s)
  2. 监控 Rpl_semi_sync_master_status,降级时立刻告警
  3. 至少配 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

三条实用建议:

  1. 只对高可用切换的目标从库开半同步,其他从库用异步
  2. 半同步从库和主库放同机房,避免跨机房抖动影响主库写入
  3. wait_point 用 AFTER_SYNC (MySQL 5.7+ 默认就是这个),
    别手动改成 AFTER_COMMIT

什么时候不用:

  • 数据丢了能重放(比如可以从消息队列重建)
  • 从库经常故障/网络不稳
  • 对写入延迟极度敏感且数据能容忍丢失

半同步是个"用一点性能换数据安全"的取舍,

在支付、订单这类场景很值得 ;

在日志、埋点这类场景没必要。

相关推荐
曦夜日长1 小时前
MySQL系统篇,安装部署(一):CentOS 7 下从卸载到登录的完整实战
数据库·mysql·centos
Anastasiozzzz1 小时前
InnoDB 为什么不用跳表,Redis 为什么不用 B+树?一场“磁盘”与“内存”的终极对决
数据库·redis·b树
Pin_Qing1 小时前
同一天,谷歌半价卖旗舰,阶跃翻倍收缓存
大数据·开发语言·人工智能·程序人生·缓存
AI源码2 小时前
PC 手机电商(MySQL 版)
数据库·mysql·智能手机
木棉软糖3 小时前
JDBC 读 ResultSet 读到一半,报 stream closed
java·mysql·spring
用户84913717547163 小时前
【技术面试高频】MySQL 存储 IP 地址:你还在用 VARCHAR?
mysql·面试
Thomas.Sir3 小时前
第29课:微服务异步调用、线程池优化 & 事件驱动开发
java·redis·微服务
java_nnnn3 小时前
Redis 初识:从单体架构到分布式架构的演进
redis·分布式·架构
壹玖玖肆4 小时前
Docker 部署 openGauss 7.0(aarch64)MySQL 兼容模式建库与数据恢复完整实践
mysql·docker·容器