按 Enter 键跳转到正文

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

再结合缓存、连接池、代理和高可用组件,逐步构建完整的数据库架构。

《MySQL 分库分表:什么时候需要,怎么设计?》

发表评论