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
2
3
4
5
6
7
Master
├── binlog dump thread:读取 binlog 发送给 Slave
└── 写入 binlog

Slave
├── IO thread:连接 Master,接收 binlog,写入 relay log
└── SQL thread:读取 relay log,重放到从库数据页

3.1 核心步骤

  1. 主库事务提交后,把变更写入 binlog。
  2. binlog dump thread 把 binlog event 推给从库 IO thread。
  3. 从库 IO thread 写入 relay log。
  4. 从库 SQL thread 按顺序读取 relay log,解析并执行。

复制是异步串行的:SQL 线程只有一条,这是主从延迟的根本原因之一。

四、复制模式:异步、半同步、全同步

模式 主库何时返回成功 数据一致性 性能 风险
异步复制 写入本地 binlog 即返回 最弱 最高 主库宕机可能丢数据
半同步复制 至少一个从库收到并写入 relay log 后返回 中等 中等 网络抖动会拖慢主库
组复制 / 全同步 多数节点确认 最强 最低 需要 Group Replication

4.1 半同步复制配置

1
2
3
4
5
6
7
8
# 主库
plugin-load=rpl_semi_sync_master=semisync_master.so
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000 # 毫秒,超时降级异步

# 从库
plugin-load=rpl_semi_sync_slave=semisync_slave.so
rpl_semi_sync_slave_enabled=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
2
3
4
5
6
[mysqld]
gtid_mode=ON
enforce_gtid_consistency=ON
log-bin=mysql-bin
binlog_format=ROW
log_slave_updates=ON

启用 GTID 后,change master to 不再需要 MASTER_LOG_FILEMASTER_LOG_POS

1
2
3
4
5
CHANGE MASTER TO
MASTER_HOST='master_host',
MASTER_USER='repl',
MASTER_PASSWORD='repl_pass',
MASTER_AUTO_POSITION=1;

六、主从延迟:原因、监控与治理

6.1 延迟来源

层级 原因 应对
主库 大事务、DDL、批量写入 拆小事务、业务低峰执行
网络 跨机房、带宽不足 就近部署、专线
从库 SQL 线程 单线程串行重放 开启并行复制
从库硬件 IO/CPU 弱于主库 从库配置不低于主库

6.2 延迟监控

1
2
SHOW SLAVE STATUS\G
-- 关注 Seconds_Behind_Master

更精确的判断:

1
SELECT MAX(@@global.gtid_executed) - MIN(@@global.gtid_executed) FROM ...;

Seconds_Behind_Master 为 0 不代表完全无延迟,只能作为趋势参考。

6.3 并行复制

MySQL 5.6 起支持按库并行,5.7 引入 slave_parallel_type=LOGICAL_CLOCK 可按事务组并行,显著提升重放速度:

1
2
3
slave_parallel_workers=8
slave_parallel_type=LOGICAL_CLOCK
slave_preserve_commit_order=ON # 5.7.19+

七、读写分离:把读流量拆出去

7.1 常见方案对比

方案 位置 优点 缺点
应用层多数据源 业务代码 最灵活 侵入代码
MyCat 中间件 成熟、插件多 社区活跃度下降
ShardingSphere JDBC/Proxy 两层 生态好、文档全 复杂 SQL 可能有限制
ProxySQL 数据库代理 性能高、规则灵活 需要额外运维

7.2 读写分离的核心策略

  • 强制走主库:写操作、事务内读、对实时性要求高的查询。
  • 允许走从库:纯读请求、报表、离线统计。
  • 延迟容忍:设置 max_slave_lag,超过阈值自动切回主库。

7.3 ProxySQL 简单示例

1
2
3
4
5
6
7
8
9
-- 添加主库 hostgroup 10,从库 hostgroup 20
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (10,'192.168.1.10',3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (20,'192.168.1.11',3306);

-- 读写分离规则
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES (1, 1, '^SELECT.*FOR UPDATE', 10, 1);
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply)
VALUES (2, 1, '^SELECT', 20, 1);

八、高可用切换: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 方案。