主从复制模式对比
提出问题
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 回放优势:主库写入延迟极低,对主库几乎没有额外开销。
致命缺陷:主库宕机时,已提交但尚未同步到从库的事务就此丢失。如果配合半同步的备升主,就会出现数据回滚甚至从库比主库多数据的诡异现象。
-- 查看当前复制模式
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_yes 和 Rpl_semi_sync_master_off 的状态,前者占比降了就报警。一般用 Rpl_semi_sync_master_yes / (Rpl_semi_sync_master_yes + Rpl_semi_sync_master_off) 算半同步成功率,低于 99.9% 就要排查。
半同步推荐配置:
# 主库
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,提交
主节点 → 客户端: OKMGR 的两种模式:
- 单主模式:只有主节点可写,其他只读,故障自动选主
- 多主模式:所有节点都可写,内置冲突检测,但性能降级明显
三种模式对比:
| 特性 | 异步 | 半同步 | 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 决定并行策略。
-- 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 有要求。
-- 开启 WRITESET 依赖追踪
SET GLOBAL binlog_transaction_dependency_tracking = WRITESET;
-- 可选值: COMMIT_ORDER(默认)| WRITESET | WRITESET_SESSIONWRITESET 的坑:WRITESET_SESSION 模式下,同一个 session 的事务不会并行,即使操作的是不同行。如果应用层用长连接批量提交,并行度反而被限制。建议用 WRITESET(不限制 session),从库配置 slave_parallel_workers = CPU 核数 / 4(64 核给 16)。
主从延迟排查清单
遇到主从延迟,按这个顺序排查:
Seconds_Behind_Master:
SHOW SLAVE STATUS看延迟秒数踩坑:这个值是从库当前时间戳减去 binlog 中记录的时间戳算出来的。如果从库系统时间被 NTP 调快了 5 分钟,
Seconds_Behind_Master先飙到 300 秒,然后 NTP 再调回来,又清零。某次线上告警就是因为这个,查了半天发现是 NTP 配置问题。正确做法:对比SHOW SLAVE STATUS中的Master_Log_File和Relay_Master_Log_File的 pos 差值,这个不依赖系统时间。主库大事务:
SHOW PROCESSLIST看长事务,一个DELETE FROM t删 1000 万行,binlog 几十 G,等从库慢慢回放解决方案:
pt-archiver分批删除,每次 1000 行,加--sleep 1控制节奏。从库硬件差:从库 CPU/IO 比主库弱,回放赶不上写入。这不是配置问题,是预算问题
并行复制没开:单线程回放,主库写入多就积压。检查
slave_parallel_workers是否为 0大表 DDL:
ALTER TABLE重建表,从库回放同样慢。用pt-online-schema-change或gh-ost来跑从库上有读负载:从库的 SELECT 查询和回放线程争抢 CPU 和 IO 资源。如果从库还在扛读流量,延迟几乎是必然的
快速定位脚本:
# 检查延迟和并行复制配置
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 章