MySQL 主从复制原理与实战
MySQL 主从复制原理与实战
当单台 MySQL 的查询压力越来越大时,一个常见的扩展方案就是增加 Replica。
例如:
Application
│
┌──────┴──────┐
↓ ↓
Write Read
↓ ↓
Primary Replica
Primary 负责写入,Replica 负责读取。
但 MySQL 并不是简单地把 Primary 的数据文件复制给 Replica,而是通过 Binlog + 复制线程 将数据变更传播到其他实例。
因此,要真正理解 MySQL 主从复制,需要先搞清楚:
Primary
↓
Binlog
↓
网络
↓
Relay Log
↓
复制线程
↓
Replica
本文从这个链路开始介绍。
1. 什么是 MySQL 主从复制
MySQL Replication 是一种将一个 MySQL 实例上的数据变更复制到其他 MySQL 实例的机制。
传统上通常称:
Master
Slave
现在更推荐使用:
Source
Replica
本文使用:
Primary
Replica
来描述。
最简单的架构:
id="q7n2pa"
Primary
│
Binlog
│
↓
Replica
Primary 执行:
INSERT
UPDATE
DELETE
产生 Binlog。
Replica 获取这些 Binlog 事件,然后执行对应的数据变更。
最终:
Primary 数据
↓
Binlog Events
↓
Replica 数据
因此 Replica 本质上是在重放 Primary 的数据变更。
2. 主从复制的核心工作流程
MySQL 复制涉及几个核心组件:
id="g6v1rs"
Primary
└── Binlog
Replica
├── Receiver
├── Relay Log
└── Applier
整个流程可以简化成:
id="n5q0bz"
Primary
│
│ ① 写入 Binlog
↓
网络
│
│ ② Replica 接收
↓
Relay Log
│
│ ③ 复制线程读取
↓
Replica 数据库
更完整一点:
id="a8o7xk"
Primary
│
Binlog File
│
│
↓
Replica Receiver
│
↓
Relay Log
│
↓
Replica Applier
│
↓
Replica Storage
不同 MySQL 版本和复制配置下具体实现存在差异,但理解这条主链路非常重要。
3. Binlog、Relay Log 和复制线程
Binlog
Binlog 位于 Primary。
例如:
id="2u7vby"
Primary
↓
事务执行
↓
Binlog
它记录数据库发生的变更事件。
Replica 并不是直接读取 Primary 的数据文件,而是通过复制协议获取 Binlog。
Relay Log
Replica 收到 Binlog 事件后,会先写入本地 Relay Log。
可以理解为:
id="9u2kqf"
Primary Binlog
↓
网络
↓
Replica
↓
Relay Log
Relay Log 相当于 Replica 上的中转日志。
复制线程
传统复制架构中通常可以理解成两个主要角色:
Receiver
↓
读取 Primary Binlog
↓
写 Relay Log
Applier
↓
读取 Relay Log
↓
执行数据变更
因此:
id="h3v9rj"
Primary
↓
Binlog
↓
Receiver
↓
Relay Log
↓
Applier
↓
Replica
当 Replica 落后时,问题可能出现在:
网络
Receiver
Relay Log
Applier
SQL 执行
中的任意环节。
4. GTID:让复制定位更简单
传统复制可以使用:
Binlog File
+
Position
来定位复制位置。
例如:
mysql-bin.000001
Position 123456
这种方式需要维护具体的 Binlog 文件和位置。
MySQL 提供了 GTID:
Global Transaction Identifier
用于给事务分配全局唯一标识。
可以简单理解成:
id="e9k4ra"
UUID:Transaction Number
例如:
id="8oj9up"
3f4d2c10-xxxx-xxxx-xxxx-xxxxxxxxxxxx:100
这样 Replica 可以通过 GTID 知道:
哪些事务已经执行
哪些事务还没有执行
复制关系可以从:
文件 + Position
转变成:
GTID
GTID 的优势
使用 GTID 后,故障切换和重新建立复制关系通常更加方便。
例如:
Primary A
↓
GTID 100 ~ 200
Replica 已经执行:
GTID 100 ~ 180
那么继续复制时就可以从:
181
开始处理。
因此 GTID 已经成为现代 MySQL 复制架构中非常重要的能力。
5. 一个最小主从复制配置
下面以 MySQL 8 为例说明基本配置思路。
假设:
Primary
10.0.0.10
Replica
10.0.0.11
Primary 配置
修改 MySQL 配置:
[mysqld]
server-id=1
log_bin=mysql-bin
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
其中:
server-id
用于标识复制拓扑中的 MySQL 实例。
每个实例应该保持唯一。
Primary
server-id=1
Replica
server-id=2
log_bin 开启 Binlog。
生产环境通常使用:
ROW
作为 Binlog 格式。
GTID 相关参数:
gtid_mode=ON
enforce_gtid_consistency=ON
用于启用 GTID 复制。
修改配置后重启 MySQL。
创建复制账号
在 Primary 创建专用账号:
CREATE USER 'repl'@'10.0.0.11'
IDENTIFIED BY 'StrongPassword';
GRANT REPLICATION REPLICA
ON *.*
TO 'repl'@'10.0.0.11';
复制账号应该使用专门的低权限账号,而不是直接使用业务账号。
6. Replica 配置
Replica:
[mysqld]
server-id=2
relay_log=relay-bin
gtid_mode=ON
enforce_gtid_consistency=ON
然后在 Replica 配置 Primary:
CHANGE REPLICATION SOURCE TO
SOURCE_HOST = '10.0.0.10',
SOURCE_USER = 'repl',
SOURCE_PASSWORD = 'StrongPassword',
SOURCE_AUTO_POSITION = 1;
启动复制:
START REPLICA;
查看状态:
SHOW REPLICA STATUS\G
重点关注:
Replica_IO_Running
Replica_SQL_Running
Seconds_Behind_Source
如果复制正常,通常可以看到:
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
这里的具体字段会根据 MySQL 版本和配置有所差异,但排查复制状态时,核心思路是不变的:
能不能从 Primary 拉到日志?
能不能正常应用日志?
7. 如何判断复制是否正常
生产环境不能只看:
Replica_IO_Running
Replica_SQL_Running
还应该关注复制延迟和错误。
例如:
SHOW REPLICA STATUS\G
重点关注:
Source_Host
Source_Log_File
Read_Source_Log_Pos
Relay_Source_Log_File
Exec_Source_Log_Pos
Seconds_Behind_Source
Last_IO_Error
Last_SQL_Error
可以简单分成三个问题。
第一:有没有连接 Primary
关注:
Replica_IO_Running
如果出现:
No
需要检查:
网络
账号权限
Primary 状态
Binlog
复制配置
第二:Replica 能不能执行日志
关注:
Replica_SQL_Running
如果是:
No
说明 Replica 在应用日志时遇到了问题。
例如:
主键冲突
表不存在
数据约束冲突
SQL 执行错误
应该重点查看:
Last_SQL_Error
第三:有没有复制延迟
例如:
Seconds_Behind_Source
可以作为延迟观察指标之一。
但是不能简单理解为:
Seconds_Behind_Source = 0就代表主从完全同步。
它受到复制线程执行状态等因素影响。
生产环境最好结合:
GTID
日志位点
业务时间戳
监控指标
综合判断复制状态。
8. 什么是主从延迟
主从复制是异步复制时,Primary 提交事务后:
Primary
↓
Binlog
↓
网络
↓
Replica
↓
执行
这个过程需要时间。
因此:
Primary
已经写入
Replica
可能还没执行
这就是复制延迟。
例如用户刚刚修改昵称:
UPDATE users
SET nickname = 'Tom'
WHERE id = 100;
Primary 已经返回:
成功
紧接着应用查询 Replica:
SELECT nickname
FROM users
WHERE id = 100;
可能暂时得到旧值。
因此读写分离系统必须考虑:
复制延迟带来的最终一致性问题。
常见解决方式
对于强一致性要求的查询:
写入后立即读取
→ Primary
普通查询:
允许短暂延迟
→ Replica
也可以根据请求上下文进行路由:
用户刚完成写操作
↓
短时间内读 Primary
普通页面查询
↓
Replica
这类策略通常称为:
Read After Write Consistency
即写后读一致性。
9. 主从复制在高可用架构中的作用
主从复制不仅可以分担查询压力,也可以作为高可用架构的基础。
例如:
Application
│
Proxy / Router
│
┌──────┴──────┐
↓ ↓
Primary Replica
│
│ Binlog
↓
Replica
正常情况下:
Primary
→ 负责写
Replica
→ 负责读
如果 Primary 故障:
Primary
×
↓
故障检测
↓
选择 Replica
↓
提升为新的 Primary
这就是故障切换(Failover)的基本思路。
但需要注意:
主从复制本身不等于完整的高可用方案。
真正的生产级高可用还需要解决:
故障检测
节点选主
VIP / Proxy / Router
应用连接切换
数据一致性
脑裂
故障恢复
因此:
Replication
≠
完整 HA
复制只是高可用架构的重要基础。
10. 总结
MySQL 主从复制最核心的链路可以记住:
Primary
│
Binlog
│
↓
Receiver
│
↓
Relay Log
│
↓
Applier
│
↓
Replica
其中:
Binlog
→ 记录 Primary 的数据变更
Receiver
→ 从 Primary 获取复制事件
Relay Log
→ Replica 本地的中转日志
Applier
→ 应用复制事件,修改 Replica 数据
GTID 则解决了传统:
Binlog File
+
Position
定位方式比较繁琐的问题,使复制拓扑管理和故障切换更加方便。
实际项目中还需要重点关注:
复制是否正常
复制是否延迟
Replica 是否出现 SQL 错误
读写分离是否允许最终一致性
故障后如何切换
可以把 MySQL 从单实例逐步扩展的过程理解为:
单机 MySQL
↓
SQL / Index 优化
↓
Buffer Pool / 事务 / 锁优化
↓
Redis 缓存
↓
Primary + Replica
↓
读写分离
↓
高可用
↓
分库分表
其中不要过早进入复杂架构。
如果单机 MySQL 通过 SQL、索引、事务、缓存等手段仍然可以满足业务需求,就没有必要为了“高并发架构”而提前引入主从、分库分表。
而当单机数据库已经成为明显瓶颈时,主从复制提供了一条比较自然的扩展路径:
写 → Primary
读 → Replica
再结合缓存、连接池、代理和高可用组件,逐步构建完整的数据库架构。
发表评论