单库内核(存储、事务、日志、优化)齐了之后,生产还要面对:副本怎么跟上、主挂了怎么切、读能不能摊到从库。分库分表是「容量扩展」主线(见 数据库扩展);本篇只讲单逻辑实例上的复制与高可用——广告配置只读扩展和计费防丢的交汇点。
本文是 MySQL 内核系列的第 5 篇(主从复制与高可用 · 收官)。 全系列 5 篇:
一句话定位:复制 = 主库 binlog 事件经 Dump/IO/SQL(或并行 worker)在从库回放;半同步换「至少一副本收到」的耐久;HA 负责探活升主;读写分离必须显式处理复制延迟。
TL;DR
- 链路:Master 写 binlog → Dump Thread → Replica IO Thread 写 relay → SQL Thread / 并行 workers 应用。
- 异步 vs 半同步:异步提交即返回,主挂可能丢未传出事务;半同步等至少一个从库 ACK(落 relay),超时可降级。
- 并行复制:按 schema / logical clock / writeset 多 worker 回放,是压延迟的主力。
- 延迟来源:网络与 IO 拉日志、SQL 回放排队、大事务、无并行、从库硬件差。
- HA:传统主从 + MHA/Orchestrator 编排切换;或 MGR / InnoDB Cluster 多数派自动选主。
- 读写分离:写主读从提吞吐,但有复制延迟——配置变更后「写后读旧」会酿错误投放。
Table of contents
Open Table of contents
1. 复制在解决什么
- 读扩展:配置/报表只读打到多从;
- 高可用:主挂提升从;
- 备份与分析:从库做备份、跑重 SQL,减少伤主;
- 异地容灾(延迟与一致性权衡另议)。
前提是 binlog 可靠且与引擎一致——所以回到 双 1 与 2PC。位点演进出 GTID,切换与对账更清晰,现代部署优先开 GTID。
复制过滤(replicate-do-table 等)、只读从库上的临时表、以及「有人在从库误开可写」都会制造漂移。广告配置若存在「从库手工改一笔救急」,后面切换成主时会变成长达数周的幽灵 bug——从库必须只读,救急走正式变更。
和 数据库扩展 的边界再说一次:
| 问题 | 本篇(复制/HA) | 扩展系列 |
|---|---|---|
| 单库读顶不住 | 加只读从 | — |
| 单库写顶不住 | 最多垂直拆分思路 | 分库分表 / 队列削峰 |
| 主挂了 | 升从、MGR | — |
| 数据量无限涨 | 归档 + 从库备份 | 分片、冷热分离 |
不要指望「多挂几个从」解决写容量——写仍然砸在主上。
2. 复制链路:三线程心智
延迟 = 拉日志延迟 + 回放排队;监控要拆开看 Seconds_Behind_Source 背后到底卡在哪段。
- Binlog Dump(主):把事件推给(或被)从库;
- IO Thread(从):收事件写入 relay log;
- SQL Thread / Coordinator+Workers(从):解析 relay,应用到存储引擎。
旧版单线程 SQL 回放在写密集计费库几乎必堆积;并行复制按库、按事务间依赖(LOGICAL_CLOCK)或 writeset 冲突检测,把无冲突事务并行落库。
大事务(一次改几十万行)是延迟杀手:即便并行,也挡不住「一个事件堵住流水线」。广告侧批量改价、刷状态要切批、控制事务大小。
ROW 格式下,没主键/合适唯一键的表在从库回放更新可能要扫大量行来定位,延迟会离谱——每张被复制的表都应有主键,这是复制场景的硬纪律,不只是范式要求。
级联复制(A→B→C)曾用于跨机房,但排障复杂、延迟叠加;今日更常见是主库扇出多个从,或用中间件/云产品做拓扑。计费库慎用「深度级联」当 HA 主路径。
并行复制调参直觉:
- worker 数要匹配从库 CPU 与可并行事务比例;
- 单库内大表连写冲突多时,writeset 也救不了——还是要拆事务、降冲突;
- 从库若跑分析查询,请与「复制专用从」隔离,避免互相抢 IO。
3. 异步 vs 半同步
半同步保证的是「至少一从库收到并落 relay」,不是「已经应用完」——读从仍可能短暂落后。
| 模式 | 提交返回时机 | 主宕机风险 |
|---|---|---|
| 异步 | 本机提交完即可 | 未传出 binlog 可能丢 |
| 半同步 | 至少一个从库 ACK | 耐久更好;从库慢拖主库;超时降级 |
rpl_semi_sync_master_wait_point:AFTER_SYNC(常见推荐)与 AFTER_COMMIT 决定 ACK 与引擎提交的相对顺序,选型时按版本文档核对。计费/账本倾向:双 1 + 半同步;纯配置只读从可接受异步,但要承认丢提交窗口。
观察半同步是否真在工作:主库状态里应能看到 ACK 客户端数;若频繁超时降级成异步,等于你以为有耐久其实没有——要告警「降级次数」。从库磁盘打满、网络抖动都会让半同步拖主库提交,此时要扩从或修盘,而不是默默忍受。
全同步(等所有从或 majority apply)延迟更高,MGR 单主模式用组复制协议做到一致性与自动选主,代价是冲突与网络更敏感。
4. 高可用拓扑
扩展容量(分片)不在本篇;这里只保证「一台逻辑主」的复制与切换。
4.1 传统主从 + 编排
MHA、Orchestrator、各类云厂商 RAS:探活 → 选最新从库 → 补齐差异 → 提升为主 → 改 VIP/DNS/代理路由。要点:
- 切换演练要常做;
- 应用连接要能快速感知(短超时 + 重试 + 幂等);
- 脑裂防护:旧主必须 fence(杀连接、只读、摘路由);
- 探活不要只 ping 端口——要确认可接受复制进度与只读状态,避免「活着但已远远落后」的从被误提。
误切换代价:短暂双主、GTID 分叉、对账几天。所以自动切换的阈值与人工确认门槛要按库的关键级分开:计费库可以「自动只读 + 人工确认升主」,配置库可以更激进。
4.2 MGR / InnoDB Cluster
组复制基于多数派共识思想(与 Raft/Paxos 系列 同类问题),故障时自动选主。适合希望少人工切换的中小集群;也要注意网络分区时的可写性、以及多主模式下冲突。广告配置库很多团队仍用「单主 + 半同步 + 编排」以求简单可控。
MGR 多主模式理论上多点可写,但冲突认证失败要回滚,广告「同行列改预算」类冲突密集,实践里少用多主扛计费写——单主更可预期。
4.3 读写分离
Proxy(如 ProxySQL、云中间件)或应用层路由:UPDATE 走主,SELECT 走从。最大坑永远是:
写后立刻读从库 → 读到旧快照。
对策按场景组合:
- 关键读粘滞主库(会话级);
- 等 GTID / 读自己的写(wait for executed GTID);
- 业务接受短暂不一致(纯报表);
- 出价、开关、预算阈值这类,写后短窗口强制读主。
也可以在中间件做「事务粘滞」:同一会话在写过后一段时间内强制走主。实现简单,但连接池/多路复用下要定义清「会话」边界,否则粘滞失效或把过多读打回主库。
三种常见架构组合(广告配置):
- 主 + 2 半同步从 + Proxy 读写分离:经典,因果读要代码兜底;
- MGR 单主 + Router:少人工切换,网络与冲突要评估;
- 云托管主从 + 只读地址:省运维,仍然逃不开写后读旧,SLO 自己定。
选型看团队熟练度与「能不能接受短暂只读」:自动切换越猛,越要保证应用幂等与连接回收。
5. GTID 与切换时要注意什么
GTID(全局事务 ID) 让「每个事务在集群里有唯一身份证」,好处:
- 换主、加点从库时,用
MASTER_AUTO_POSITION一类能力自动找位点,少手算 file/pos; - 应用可记录「我刚写下的 GTID」,再
WAIT_FOR_EXECUTED_GTID_SET做写后读从; - 排查「某个事务有没有上从库」更直观。
切换检查单(无论 MHA 还是人工):
- 确认旧主已 fence(只读或下线路由),防脑裂双写;
- 选中的新主 relay/复制进度够新(半同步场景通常更安心);
- 代理 / DNS / VIP 原子切到新主;
- 应用连接排空与重试;幂等写避免「超时已成功又重放」;
- 从库重新挂到新主,并验证 GTID 集合一致。
云托管 RDS 把很多步骤产品化了,但应用层幂等与连接失效处理仍然是你自己的活——和 高可用实践 同一套心智。
切主后常见「应用层症状」:
- 连接池仍指向旧主 → 写入失败或写到只读;
- 重试把同一扣费打两次 → 无幂等键则双扣;
- DNS TTL 过长 → 流量切换迟滞。
所以 HA 不只是 DBA 脚本,是应用连接策略 + 幂等键 + 观测三件套。
6. 延迟治理清单
- 开并行复制并验证 worker 数;
- 消灭超大事务与无主键表全表更新(无主键的 row 更新可能扫全表回放);
- 从库硬件不低于主太多(磁盘与 CPU);
- 半同步 ACK 慢时区分「网络 / 从库刷盘 / 主库等待」;
- 监控:
Seconds_Behind_Source、relay 积压、worker 活跃度、半同步状态与降级次数; - 只读从库别跑比主库更重的「偷偷全表分析」,否则自己制造延迟。
Seconds_Behind_Source 是估算值,极端时钟或多线程回放下可能抖;结合 relay 未应用字节数看更稳。
延迟跑高时的最短路径:
- 看是 IO 线程落后还是 SQL/worker 积压;
SHOW PROCESSLIST/ performance_schema 找从库正在回放的大事务;- 对照主库 binlog 是否刚暴增(批量刷数、大 DDL);
- 必要时临时把分析流量挪走,给复制让 IO;
- 根治仍是:拆大事务、保主键、并行复制、硬件对齐。
系列地图(回顾)
| 篇 | 核心问题 |
|---|---|
| InnoDB 与 B+ 树 | 行怎么存、索引怎么找 |
| MVCC 与锁 | 并发怎么看见对的版本、冲突怎么串行 |
| redo/undo/binlog | 提交如何 crash-safe、如何可复制 |
| 执行计划 | 慢在哪、怎么改计划 |
| 本篇 | 副本怎么跟、主挂怎么切、读如何摊 |
参考
- MySQL 官方文档. Replication
- MySQL 官方文档. Semisynchronous Replication
- MySQL 官方文档. MySQL Group Replication
- MySQL 官方文档. Replication Channels / Multi-Threaded Slaves(并行复制相关章节随版本演进)
- MySQL 官方文档. GTID
- MHA / Orchestrator 等开源项目文档:故障切换实践。