MySQL 主从复制与读写分离:binlog、GTID 与高可用实战

MySQL 主从复制与读写分离:binlog、GTID 与高可用实战
神经蛙“线上读写都在一台 MySQL,CPU 飙到 90% 怎么办?””主库挂了,多久能切换?”主从复制和读写分离是 MySQL 横向扩展与容灾的必经之路。本文从复制原理到生产落地一次性讲透。
一、为什么需要主从复制
单节点 MySQL 同时承担读和写时,随着流量增长,很快会碰到三大瓶颈:
| 瓶颈 | 表现 | 影响 |
|---|---|---|
| 读压力 | 查询量过大,CPU/内存/IO 飙升 | 影响写入稳定性 |
| 可用性 | 单点故障导致整个业务不可用 | RTO 无法保证 |
| 备份 | 在业务库上直接做全量备份 | 锁表、抖动、影响线上 |
主从复制(Replication)把主库(Master)的数据实时同步到一个或多个从库(Slave),做到:
- 读扩展:把读流量分散到从库。
- 高可用:主库故障时,从库可快速切换。
- 离线备份:在从库上做备份、归档、报表,不打扰主库。
二、复制依赖:binlog 与 relay log
MySQL 复制本质上是基于 binlog 的逻辑日志复制。主库把变更记录到 binlog,从库读取并重放。
2.1 binlog 的三种格式
| 格式 | 记录内容 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| STATEMENT | 原 SQL 语句 | 日志小 | 某些语句(如 UUID、触发器)会导致主从不一致 | 简单 OLAP、兼容旧版本 |
| ROW | 每行变更前后镜像 | 精确、安全 | 日志大、写密集时 IO 高 | 生产默认推荐 |
| MIXED | 默认 STATEMENT,不安全时切换 ROW | 折中 | 行为不易预测 | 逐步迁移过渡 |
生产环境推荐固定为 ROW:
1 | SET GLOBAL binlog_format = 'ROW'; |
2.2 relay log 与线程协作
从库接收 binlog 后不是直接执行,而是先写入 relay log(中继日志),再由 SQL 线程读取重放。这样即使网络抖动,也不会阻塞主库。
三、主从复制原理:三个线程的故事
一条 DML 从主库写入到从库生效,主要经过三条线程:
1 | Master |
3.1 核心步骤
- 主库事务提交后,把变更写入 binlog。
- binlog dump thread 把 binlog event 推给从库 IO thread。
- 从库 IO thread 写入 relay log。
- 从库 SQL thread 按顺序读取 relay log,解析并执行。
四、复制模式:异步、半同步、全同步
| 模式 | 主库何时返回成功 | 数据一致性 | 性能 | 风险 |
|---|---|---|---|---|
| 异步复制 | 写入本地 binlog 即返回 | 最弱 | 最高 | 主库宕机可能丢数据 |
| 半同步复制 | 至少一个从库收到并写入 relay log 后返回 | 中等 | 中等 | 网络抖动会拖慢主库 |
| 组复制 / 全同步 | 多数节点确认 | 最强 | 最低 | 需要 Group Replication |
4.1 半同步复制配置
1 | # 主库 |
半同步复制不能保证从库 SQL 线程已经执行,只能保证 relay log 已落盘。如果业务对从库读到最新数据有强要求,需要配合读写分离策略。
五、GTID 复制:让切换更省心
5.1 什么是 GTID
GTID(Global Transaction Identifier)为每个已提交事务分配全局唯一 ID:server_uuid:transaction_id。主库和从库的 GTID 集合一致,表示数据一致。
5.2 GTID 复制的优势
- 切换简单:无需手动找 binlog 文件名和位点。
- 避免位点错误:主从切换后从库直接根据 GTID 集合自动定位。
- 故障恢复可追溯:通过
Executed_Gtid_Set判断哪些事务已执行。
5.3 GTID 基础配置
1 | [mysqld] |
启用 GTID 后,change master to 不再需要 MASTER_LOG_FILE 和 MASTER_LOG_POS:
1 | CHANGE MASTER TO |
六、主从延迟:原因、监控与治理
6.1 延迟来源
| 层级 | 原因 | 应对 |
|---|---|---|
| 主库 | 大事务、DDL、批量写入 | 拆小事务、业务低峰执行 |
| 网络 | 跨机房、带宽不足 | 就近部署、专线 |
| 从库 SQL 线程 | 单线程串行重放 | 开启并行复制 |
| 从库硬件 | IO/CPU 弱于主库 | 从库配置不低于主库 |
6.2 延迟监控
1 | SHOW SLAVE STATUS\G |
更精确的判断:
1 | SELECT MAX(@@global.gtid_executed) - MIN(@@global.gtid_executed) FROM ...; |
6.3 并行复制
MySQL 5.6 起支持按库并行,5.7 引入 slave_parallel_type=LOGICAL_CLOCK 可按事务组并行,显著提升重放速度:
1 | slave_parallel_workers=8 |
七、读写分离:把读流量拆出去
7.1 常见方案对比
| 方案 | 位置 | 优点 | 缺点 |
|---|---|---|---|
| 应用层多数据源 | 业务代码 | 最灵活 | 侵入代码 |
| MyCat | 中间件 | 成熟、插件多 | 社区活跃度下降 |
| ShardingSphere | JDBC/Proxy 两层 | 生态好、文档全 | 复杂 SQL 可能有限制 |
| ProxySQL | 数据库代理 | 性能高、规则灵活 | 需要额外运维 |
7.2 读写分离的核心策略
- 强制走主库:写操作、事务内读、对实时性要求高的查询。
- 允许走从库:纯读请求、报表、离线统计。
- 延迟容忍:设置
max_slave_lag,超过阈值自动切回主库。
7.3 ProxySQL 简单示例
1 | -- 添加主库 hostgroup 10,从库 hostgroup 20 |
八、高可用切换:MHA 与 Orchestrator
8.1 MHA(Master High Availability)
传统 MySQL 高可用方案,自动完成主库故障检测、新主库选举、VIP 漂移、其余从库重新指向。
适用场景:5.7 及之前版本,配合 VIP 脚本。
8.2 Orchestrator
GitHub 开源的 MySQL 拓扑管理与可视化工具:
- 自动探测复制拓扑。
- Web UI 可视化主从链。
- 支持 hook 脚本,可集成 Consul/DNS 做服务发现。
- 可以进行 graceful 切换(planned promotion)和故障切换(emergency promotion)。
8.3 切换时的一致性问题
- 异步复制下,主库宕机可能丢失未同步到从库的事务。
- 半同步复制 + GTID 可在切换后确认是否补齐数据。
- 核心指标:RPO(数据丢失量)和 RTO(恢复时间)。
九、生产避坑清单
| 坑 | 后果 | 建议 |
|---|---|---|
| 从库配置低于主库 | 延迟越拉越大 | 从库 CPU/内存/磁盘对齐主库 |
| 写从库 | 主从不同步 | 从库设置 read_only=1 |
| 大事务同步 | Seconds_Behind_Master 飙升 | 拆分批量写入 |
| 跨机房复制 | 网络抖动、延迟 | 专线或就近部署 |
| 无 GTID | 切换时找位点痛苦 | 5.7+ 尽量启用 GTID |
| 不监控复制延迟 | 读到旧数据引发业务 bug | 监控 Seconds_Behind_Master + GTID gap |
十、总结
MySQL 主从复制是数据库扩展和容灾的基石:
- 用 binlog + ROW 格式 保证复制安全。
- 用 半同步复制 + GTID 平衡一致性与性能。
- 用 并行复制 + 硬件对齐 降低延迟。
- 用 读写分离中间件 把读压力拆出去。
- 用 Orchestrator / MHA 实现故障自动切换。
复制不是银弹,它解决的是读扩展和高可用,不是无限写扩展。当写流量单库扛不住时,就需要考虑分库分表、TiDB 等 NewSQL 方案。











