MySQL - 主从复制与读写分离
MySQL - 主从复制与读写分离
主从复制让一台(或多台)从库持续同步主库的数据变更,是读写分离、高可用、备份与容灾的基础。理解三个线程的协作、binlog 格式、复制模式以及延迟与一致性的取舍,是使用 MySQL 复制的起点。
1. 复制原理:三个线程
MySQL 复制(异步)围绕三类线程协作,核心流程如下:
- 主库的 dump 线程(binlog dump thread):主库把数据变更写入 binlog(二进制日志);当从库连上来请求日志时,主库为每个从库启动一个 dump 线程,把 binlog 事件发送给从库。
- 从库的 I/O 线程:连接主库,接收 binlog 事件,写入本地的 relay log(中继日志),并记录已读到的位置。
- 从库的 SQL 线程:读取 relay log,把其中的事件在从库上重放,使数据最终与主库一致。
主库: 应用写入 -> binlog
| (dump 线程推送)
v
从库: I/O 线程 -> relay log -> SQL 线程重放 -> 从库数据从库上可通过 SHOW SLAVE STATUS\G(MySQL 8.0.22 起为 SHOW REPLICA STATUS)观察 Slave_IO_Running 与 Slave_SQL_Running 两个线程状态。
2. binlog 格式
| 格式 | 记录内容 | 优点 | 缺点 |
|---|---|---|---|
STATEMENT | SQL 语句原文 | 日志小 | 依赖执行上下文,NOW()、UUID() 等非确定性语句易导致主从数据不一致 |
ROW | 每一行数据的变更 | 最安全、一致性好 | 日志量大,批量更新时膨胀明显 |
MIXED | 按语句自动选择 | 兼顾两者 | 仍需理解切换规则 |
生产环境普遍推荐 ROW:它记录的是“哪行变成了什么”,可重放性强,也是 binlog 消费、CDC 同步的基础。设置项为 binlog_format=ROW。
3. 复制模式
| 模式 | 行为 | 一致性 | 性能 |
|---|---|---|---|
| 异步复制(默认) | 主库提交后立即返回,不等从库确认 | 弱:主库宕机可能丢数据 | 最高 |
| 半同步复制 | 主库等待至少一个从库接收并写入 relay log 后才返回 | 较强(减少丢失窗口) | 略有下降 |
| 组复制 / 全同步(MGR) | 基于 Paxos 的一致性协议,多数派确认 | 强 | 延迟更高、结构更复杂 |
现代实践中,GTID(全局事务标识) 是标配:每个事务有全局唯一 ID,切换主库时无需手工计算 binlog 文件名与位点,显著简化故障转移。开启 gtid_mode=ON 后用 GTID 建立复制即可。
4. 复制延迟与一致性
复制是单向、异步的:从库数据天然可能落后于主库,Seconds_Behind_Master 反映大致延迟(该值在某些场景会失真,仅供参考)。延迟的常见原因:
- 从库 单线程 SQL 重放跟不上主库的并发写入(原生 MySQL 5.6+ 支持并行复制,可按库或按事务组并行)。
- 大事务、DDL、大批量
UPDATE/DELETE。 - 从库硬件或负载不足。
读写分离下的经典一致性问题:写主库后立即读从库,可能读不到刚写入的数据(“读到旧值”)。常见对策:
- 对实时性要求高的读,强制走主库。
- 基于 GTID/binlog 位点做会话级一致性读(读到不早于某事务的位点)。
- 用缓存或“写后延迟再读”等业务补偿。
更多一致性相关的机制参见 MVCC。
5. 搭建与关键参数
主库:
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog_format = ROW
gtid_mode = ON
enforce_gtid_consistency = ON从库:
[mysqld]
server-id = 2
relay-log = relay-bin
read_only = ON建立复制(MySQL 8.0 语法):
-- 从库执行
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='master-ip',
SOURCE_USER='repl',
SOURCE_PASSWORD='***',
SOURCE_AUTO_POSITION=1; -- 基于 GTID
START REPLICA;主库需为复制账号授权 REPLICATION SLAVE 权限。
6. 读写分离的实现
读写分离由代理层或应用层决定把请求发往主库还是从库:
- 代理/中间件:如各类 MySQL 代理与分库分表中间件,接收请求后按读写类型路由。
- 应用层:配合多数据源 + AOP,按方法名/注解决定走主库或从库。
- 云数据库:云厂商通常提供只读实例与自动读写分离代理。
无论哪种方式,都要处理好“强制走主库”的逃生通道,避免主从延迟影响用户看到自己刚提交的数据。
7. 常见故障
- 复制中断:
Slave_SQL_Running=No。常见错误码1062(主键/唯一键冲突)、1032(找不到行)。可通过SHOW SLAVE STATUS中的Last_SQL_Error定位。 - 主从数据不一致:多因
STATEMENT格式、非确定性函数或人工在从库写入。建议用校验工具定期比对(如 pt-table-checksum)。 - 从库延迟高:排查大事务、并行复制配置与从库负载。
- GTID 报错:切换或误写入导致 GTID 空洞,需按官方步骤修复。
8. 主从切换与高可用
主从复制只解决“数据同步”,不解决“主库挂了怎么办”。生产环境通常配合高可用组件实现自动故障转移:
- 故障检测:探测主库是否存活。
- 选主:从多个从库中选出数据最新、延迟最小的一个,提升为新主库。
- 流量切换:把写流量指向新主库,并让其他从库改指向新主库。
- 数据补偿:处理尚未同步完的差异。
GTID 的引入让切换大幅简化:使用 SOURCE_AUTO_POSITION=1 的从库能自动寻找新主库缺失的事务,无需人工计算 binlog 文件名与位点;没有 GTID 时,切换要仔细核对位点,极易出错。
半同步的意义
半同步复制通过“主库等待至少一个从库确认接收”来缩短主库宕机时的数据丢失窗口,牺牲一点写入延迟换取更强一致性,是核心业务常见的折中。但要注意:半同步的“确认”是“已写入从库 relay log”,并非“已应用”,仍存在极小的不一致可能。
9. 复制拓扑与扩展
- 一主多从:读扩展的基础,用从库分担读流量。
- 级联复制(主 → 从 → 从):减轻主库的 dump 压力,适合从库较多的场景,但会放大延迟。
- 双主/多主:用于特殊场景,必须严格避免双写冲突,一般配合自增步长错开或分布式 ID。
无论哪种拓扑,都要保证 server-id 唯一、时钟同步,并对每条复制链路做监控告警。故障切换后还要考虑:旧主库恢复后如何重新加入复制、如何处理它多出或缺失的事务。
10. 小结
- 复制由主库 dump 线程、从库 I/O 线程与 SQL 线程协作完成,数据经 binlog → relay log 重放。
- binlog 生产环境推荐
ROW;复制模式分异步、半同步、组复制,GTID 是现代标配。 - 复制是异步的,存在延迟;读写分离要处理“读到旧数据”,并提供强制走主库的通道。
- 常见故障集中在复制中断、数据不一致与延迟偏高,需靠监控与校验工具兜底。