Skip to content

主从复制模式对比

提出问题

MySQL 主从复制是高可用架构的基石,但「复制延迟丢数据」几乎是每个 DBA 都踩过的坑。默认异步复制性能好但主库宕机可能丢事务,半同步复制保证不丢数据却可能拖慢写入。面试官问这个问题,不只是想听你背三种模式的定义,更想看你是否理解背后的取舍:一致性、可用性、性能,你到底选哪个? 生产上你遇到过主从延迟几分钟的破事,怎么排查、怎么治?

分析问题

异步复制(Asynchronous Replication)

这是 MySQL 默认的复制方式。主库提交事务后直接返回客户端,不等待从库确认。Binlog 通过 Dump Thread 推送给从库的 I/O Thread,写入 Relay Log 后由 SQL Thread 回放。

时序流程

客户端 → 主库: 提交事务
主库 → 存储: 写 redo log + binlog (prepare/commit)
主库 → 客户端: OK (不等从库)
主库 → 从库: Dump Thread 推送 binlog event
从库 → 从库: I/O Thread 写入 relay log
从库 → 从库: SQL Thread 回放

优势:主库写入延迟极低,对主库几乎没有额外开销。

致命缺陷:主库宕机时,已提交但尚未同步到从库的事务就此丢失。如果配合半同步的备升主,就会出现数据回滚甚至从库比主库多数据的诡异现象。

sql
-- 查看当前复制模式
SHOW VARIABLES LIKE 'rpl_semi_sync%';
-- 异步模式下 rpl_semi_sync_master_enabled = OFF

生产事故案例:某电商公司双十一大促,主库突然宕机,VIP 强制切换到从库。切换后发现从库少了最后 3 秒的 2000+ 笔订单数据。团队花了 4 小时用 binlog 反向解析、手工补单。事后复盘根因:异步复制模式下,主库崩溃前那批订单的 binlog 还没推送到从库。解决方案:改半同步 + 设置 rpl_semi_sync_master_timeout = 5000(5 秒超时降级并告警),同时核心交易链路强制走主库读。

半同步复制(Semisynchronous Replication)

MySQL 5.5 引入,5.7 大幅增强。主库提交事务后,必须等待至少一个从库确认收到 Binlog 事件(写入 Relay Log)才返回客户端。

半同步时序流程(AFTER_SYNC)

客户端 → 主库: 提交事务
主库 → 存储: 写 redo log (prepare)
主库 → 存储: 写 binlog
主库 → 从库: 发送 binlog event (在此处等待 ack)
从库 → 主库: ACK (已写入 relay log)
主库 → 存储: commit redo log
主库 → 客户端: OK

关键区别:ACK 在 commit 之前。如果主库在 ACK 之后、commit 之前崩溃,其他节点看到的是未提交事务,可以回滚,不会出现"客户端以为成功了但数据丢了"的尴尬。

关键参数

  • rpl_semi_sync_master_timeout:超时后自动降级为异步(默认 10 秒),避免主库阻塞
  • rpl_semi_sync_master_wait_point:AFTER_SYNC(5.7 默认)比 AFTER_COMMIT 更安全,故障时主库可回滚未确认事务

AFTER_SYNC vs AFTER_COMMIT 对比

特性AFTER_SYNC (5.7+)AFTER_COMMIT (5.5/5.6)
Binlog 写入时机写 binlog 后等待commit 后等待
主库崩溃时未确认事务可回滚已 commit 事务可能已发客户端但丢失
客户端感知延迟从库应答而非 commit 时间延迟 commit 时间
实际推荐这是默认选项,别改5.6 兼容,不要在新项目用

真实场景:半同步不是「数据绝对不丢」。如果超时降级回异步,并且降级后主库立即宕机,数据仍然可能丢失。具体场景:

时间线:
T0: 主库写入事务 T1,等待从库 ACK
T1: 从库网络闪断,3 秒后超时
T2: 主库降级为异步复制(rpl_semi_sync_master_off 计数器 +1)
T3: 主库写入事务 T2,异步模式直接返回
T4: 主库宕机,T2 的 binlog 未同步到从库
T5: 从库升主,T2 丢失

所以生产上要监控 Rpl_semi_sync_master_yesRpl_semi_sync_master_off 的状态,前者占比降了就报警。一般用 Rpl_semi_sync_master_yes / (Rpl_semi_sync_master_yes + Rpl_semi_sync_master_off) 算半同步成功率,低于 99.9% 就要排查。

半同步推荐配置

ini
# 主库
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 5000  # 5秒超时,别用默认10秒
rpl_semi_sync_master_wait_point = AFTER_SYNC
rpl_semi_sync_master_wait_for_slave = ON  # 等从库 ACK,不急着返回

# 从库
rpl_semi_sync_slave_enabled = 1

全同步与 MGR

全同步复制(Group Replication,MGR)基于 Paxos 变种,事务需要多数派节点确认才能提交。这是真正的强同步,不会丢数据,但写入延迟受网络影响大。

MGR 写入时序(3 节点)

客户端 → 主节点: 写入事务
主节点 → 所有节点: 发送 binlog event
节点2 → 主节点: ACK
节点3 → 主节点: ACK
主节点: 收到多数派(2/3)ACK,提交
主节点 → 客户端: OK

MGR 的两种模式:

  • 单主模式:只有主节点可写,其他只读,故障自动选主
  • 多主模式:所有节点都可写,内置冲突检测,但性能降级明显

三种模式对比

特性异步半同步MGR
数据一致性最终一致,可能丢最终一致,基本不丢强一致
写入延迟0-1ms 增加1-5ms 增加(网络好)2-10ms 增加(网络好)
写入吞吐100%约 85-95%约 60-80%
网络要求不高高,<5ms RTT
自动故障转移需外部工具(MHA/Orchestrator)需外部工具内置
推荐场景日志/报表/可接受少量丢失大多数业务金融/支付/强一致

Binlog 三种格式与并行复制

主从延迟的根因常常出在 Binlog 格式和并行复制配置上。

Binlog 格式

格式说明日志量推荐
STATEMENT记录 SQL,非确定性函数出问题,比如 NOW() 在从库上回放得到不同时间
ROW记录行变更,每条 UPDATE 改 1000 行就产生 1000 条 event,日志量大但准确✅ 推荐,5.7+ 默认
MIXED自动判断,但 STATEMENT 模式下 UUID() 等函数还是会被当 STATEMENT 记录,有坑❌ 不如直接 ROW

踩坑:某次生产上从库延迟 20 分钟,排查发现 binlog_format = MIXED,有个 UPDATE t SET count = count + 1 WHERE ... 影响了 1 万行,被当做 STATEMENT 记录,但 count 在主库上每次执行结果不同,从库回放时 count 值不一致,导致行锁等待。改成 ROW 格式后恢复正常。

并行复制(5.7+)slave_parallel_workers 设置并行回放线程数,slave_parallel_type 决定并行策略。

sql
-- 5.7 推荐配置
SET GLOBAL slave_parallel_workers = 4;
SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';
-- 8.0 可选 WRITESET,更激进的并行度
SET GLOBAL slave_parallel_type = 'WRITESET';

WRITESET 允许同一库不同行的变更并行回放,比 LOGICAL_CLOCK 的并行度更高,但对主库的 binlog_transaction_dependency_tracking 有要求。

sql
-- 开启 WRITESET 依赖追踪
SET GLOBAL binlog_transaction_dependency_tracking = WRITESET;
-- 可选值: COMMIT_ORDER(默认)| WRITESET | WRITESET_SESSION

WRITESET 的坑WRITESET_SESSION 模式下,同一个 session 的事务不会并行,即使操作的是不同行。如果应用层用长连接批量提交,并行度反而被限制。建议用 WRITESET(不限制 session),从库配置 slave_parallel_workers = CPU 核数 / 4(64 核给 16)。

主从延迟排查清单

遇到主从延迟,按这个顺序排查:

  1. Seconds_Behind_MasterSHOW SLAVE STATUS 看延迟秒数

    踩坑:这个值是从库当前时间戳减去 binlog 中记录的时间戳算出来的。如果从库系统时间被 NTP 调快了 5 分钟,Seconds_Behind_Master 先飙到 300 秒,然后 NTP 再调回来,又清零。某次线上告警就是因为这个,查了半天发现是 NTP 配置问题。正确做法:对比 SHOW SLAVE STATUS 中的 Master_Log_FileRelay_Master_Log_File 的 pos 差值,这个不依赖系统时间。

  2. 主库大事务SHOW PROCESSLIST 看长事务,一个 DELETE FROM t 删 1000 万行,binlog 几十 G,等从库慢慢回放

    解决方案pt-archiver 分批删除,每次 1000 行,加 --sleep 1 控制节奏。

  3. 从库硬件差:从库 CPU/IO 比主库弱,回放赶不上写入。这不是配置问题,是预算问题

  4. 并行复制没开:单线程回放,主库写入多就积压。检查 slave_parallel_workers 是否为 0

  5. 大表 DDLALTER TABLE 重建表,从库回放同样慢。用 pt-online-schema-changegh-ost 来跑

  6. 从库上有读负载:从库的 SELECT 查询和回放线程争抢 CPU 和 IO 资源。如果从库还在扛读流量,延迟几乎是必然的

快速定位脚本

bash
# 检查延迟和并行复制配置
mysql -e "SHOW SLAVE STATUS\G" | grep -E "Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running|Master_Log_File|Relay_Master_Log_File|Exec_Master_Log_Pos|slave_parallel_workers|slave_parallel_type"

# 查看当前回放线程状态
mysql -e "SHOW PROCESSLIST" | grep "System lock"

# 查看大事务(binlog 大小 > 1GB 的事务)
mysql -e "SELECT THREAD_ID, EVENT_NAME, SQL_TEXT, ROWS_EXAMINED, ROWS_AFFECTED, CREATED_TMP_TABLES FROM performance_schema.events_statements_history WHERE SQL_TEXT IS NOT NULL ORDER BY ROWS_AFFECTED DESC LIMIT 10"

面试连环追问

面试官可能会接着问:

Q: 半同步降级异步后,怎么保证数据不丢? A: 不能保证。降级发生在网络抖动或从库压力大时,此时主库写入的数据在降级窗口内有丢失风险。兜底方案:核心数据写主库之后,通过 RocketMQ 异步发一条"已落库"消息,由消费端确认从库同步完成。如果超时未确认,触发补偿重试。

Q: MySQL 8.0 的异步复制有改进吗? A: 8.0 引入了 replica_parallel_workers(替代 slave_parallel_workers)和 binlog_transaction_dependency_tracking = WRITESET,并行度从 LOGICAL_CLOCK 的"同一 commit 时间窗口内"扩展到"操作不同行即可并行",延迟改善明显。另外 8.0.17+ 支持 Clone Plugin,新增节点不需要手动拷贝数据文件。

Q: 你们线上用几台机器,一主多从怎么配? A: 一主两从,一个从库做实时读(半同步,rpl_semi_sync_master_wait_for_slave = 1),另一个从库做异步备份(rpl_semi_sync_master_enabled = 0,跑 mysqldump 和定时分析查询)。主库挂了之后,用 Orchestrator 自动选主,优先选半同步的从库,因为它数据最新。

总结

三种复制模式的选择本质是 一致性 vs 性能的权衡

  • 异步复制:性能最好,但故障可能丢数据,适合日志、报表等可接受少量丢失的场景
  • 半同步复制:大多数业务的默认选择,配合监控降级报警,损失 1-3ms 延迟换取不丢数据
  • MGR 全同步:强一致性要求下使用,但网络延迟敏感,写入放大 2-3 倍

面试话术示例:「我们线上用半同步 AFTER_SYNC 模式,超时 5 秒降级异步并告警。从库开启 WRITESET 并行复制,64 核机器给 16 个回放线程。主从延迟超过 5 秒就自动切 Ganglia 告警,RDS 的延迟监控也有。之前遇到过一次大事务删历史数据导致延迟 20 分钟,后来改成分批删除 + pt-archiver 了。」

参考

参考:MySQL 官方文档 — Replication;《MySQL 实战 45 讲》第 11-14 讲;《高性能 MySQL》第 10 章

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。