返回首页

Redis架构篇5.1: 主从复制原理

适用版本:Redis 7.2.x

Redis 的高可用体系里,主从复制几乎是最基础的一环。很多同学第一次接触 Redis 架构时,会先看到这样的部署方式:一台主库负责写,一台或多台从库负责同步数据、分担读流量、参与故障切换。

看起来很简单,但如果继续往下问,就会出现很多关键问题:

  • 从库是怎么拿到主库数据的?
  • 主库数据量很大时,同步是不是会很重?
  • 网络闪断后,为什么有时只补一小段数据,有时却要全量重传?
  • 主从复制为什么能支撑 Sentinel 和 Cluster 的故障切换?

这篇文章就围绕这些问题,系统梳理 Redis 主从复制的工作原理、关键配置、常见命令和排查思路。读完之后,你应该能把“主从复制”从会用,提升到真正理解它的运行机制。


一、为什么 Redis 需要主从复制

单机 Redis 虽然简单,但一旦主机故障,服务就会直接中断。另外,随着业务读请求增加,只靠一个实例也很容易成为瓶颈。

所以主从复制主要解决三类问题:

1. 数据冗余

主库上的数据会同步到从库,相当于保留了多份副本。即使主库故障,也还有其他节点持有数据。

2. 读写分离

很多业务会把写请求打到主库,把部分读请求打到从库,从而减轻主库压力。

3. 高可用基础能力

无论是 Sentinel 还是 Redis Cluster 的副本切换,本质上都依赖主从复制。没有副本,就谈不上故障切换。

可以先记住一句话:

主从复制不是完整高可用方案,但它是 Redis 高可用的底座。


二、主从复制的基本架构图景

一个最常见的 Redis 主从拓扑如下:

                 +----------------+
                 |   Client 写入   |
                 +--------+-------+
                          |
                          v
                  +---------------+
                  |   Master 主库  |
                  |  负责处理写请求 |
                  +---+--------+---+
                      |        |
          复制流      |        |      复制流
                      v        v
               +----------+  +----------+
               | Replica1 |  | Replica2 |
               |   从库    |  |   从库    |
               +----------+  +----------+

在这个模型里:

  • 主库(master) 负责处理写请求;
  • 从库(replica) 默认从主库异步复制数据;
  • 从库也可以继续挂下级副本,但生产里更常见的是星型结构;
  • Redis 7.2.x 文档与配置中更常见的术语是 replica,但很多历史资料仍习惯写 slave

需要特别注意:

Redis 复制默认是异步复制

这意味着主库返回客户端写成功时,数据不一定已经同步到所有从库。所以主从复制增强了可用性,但并不等于强一致。


三、从库如何建立复制关系

1. 配置方式

最常见的方式是在从库配置文件中指定主库地址:

replicaof 192.168.10.10 6379
masterauth your_master_password
masteruser default

如果主库开启了 ACL 或密码认证,通常要同时配置认证信息。

2. 运行时动态指定

也可以在从库上动态执行:

127.0.0.1:6380> REPLICAOF 192.168.10.10 6379
OK

取消复制关系则可以执行:

127.0.0.1:6380> REPLICAOF NO ONE
OK

这个命令的含义是:当前实例不再作为别人的副本,而是提升为独立主库。

3. 建链后的第一步:握手

从库连接主库后,并不是立刻开始传输数据,而是会先完成一轮握手,包括:

  1. 建立 TCP 连接;
  2. 如果开启认证,执行认证;
  3. 发送 PING 检查连通性;
  4. 通过 PSYNC 申请同步;
  5. 主库根据条件决定执行全量同步还是部分重同步。

所以主从复制的关键,并不只是“从库连上主库”,而是主从双方如何决定同步方式


四、全量同步:第一次复制时发生了什么

第一次建立复制关系时,通常会执行全量同步

1. 全量同步的触发场景

以下情况常常会触发全量同步:

  • 新从库第一次接入主库;
  • 从库断线太久,主库 backlog 已经覆盖不到它缺失的数据;
  • 主库发生重启或复制历史切换,旧的复制 ID 无法继续匹配;
  • 手工重建复制关系。

2. 全量同步流程

完整流程可以概括为:

从库发起 PSYNC
        |
        v
主库判断:无法部分同步
        |
        v
主库执行 BGSAVE 生成 RDB 快照
        |
        v
把 RDB 文件发给从库
        |
        v
从库清空旧数据并加载 RDB
        |
        v
主库把快照期间积压的写命令继续发给从库
        |
        v
主从进入稳定增量复制状态

3. 为什么全量同步成本高

全量同步的压力通常体现在三个方面:

  • 主库压力:需要 fork 子进程生成 RDB;
  • 网络压力:需要传输整份快照;
  • 从库压力:要加载快照并重建数据集。

如果主库数据量很大,或者短时间内多个从库一起重连,就可能带来明显抖动。

4. 一个很实用的理解角度

全量同步本质上是:

先把主库当前的“全量状态”给从库,再把这段时间新增的写操作补上。

这样从库最终就能追平到主库最新状态。


五、部分重同步:为什么网络闪断后不一定全量复制

如果每次网络抖动都重传整份数据,Redis 主从复制会非常脆弱。为了解决这个问题,Redis 提供了部分重同步机制。

1. 核心概念:Replication ID 与 Offset

理解部分重同步,重点抓住两个概念:

(1)Replication ID

主库会维护复制历史标识,可以理解为“这一段复制历史是谁生成的”。

(2)Offset

主库复制流中的每个字节都有偏移量,从库会记录自己已经处理到哪里。

所以从库断线重连时,会告诉主库:

  • 我之前跟的是哪个复制历史;
  • 我已经同步到哪个 offset。

如果主库判断这段历史还有效,而且缺失的数据还在 backlog 中,就可以直接把缺失部分补给从库,而不必全量重传。

2. 什么是 backlog

repl-backlog-size 配置的是主库用于复制的积压缓冲区,本质上是一个环形缓冲区

repl-backlog-size 256mb

它保存的是最近一段时间的写命令流。

这块缓冲区很关键,因为部分重同步能不能成功,很大程度取决于:

  • 从库断开了多久;
  • 主库写入速度有多快;
  • backlog 是否足够大。

3. 一个直观例子

假设:

  • 主库当前复制 offset 为 1000000
  • 从库断线前同步到了 998000
  • backlog 中还保留着 998001 ~ 1000000 这段数据。

那么从库重连时,主库就只需要补发这 2000 字节之后的差量数据,而不是整库重传。

这就是部分重同步的价值:

把“全量复制”尽量变成“断点续传”。


六、复制稳定运行时,数据是怎么持续同步的

当主从进入稳定状态后,主库会把写命令异步发送给从库。

1. 主库处理写请求

客户端写入主库,例如:

SET order:1001 paid
INCR page:view:home
HSET user:1 name Alice age 20

2. 主库传播复制流

这些写操作会被编码成 Redis 协议流,持续发送给从库。

3. 从库按顺序回放

从库接收到这些命令后,按顺序应用到自己的数据集。

所以从库本质上并不是“定时拉快照”,而是:

  • 初始阶段拿一份全量快照;
  • 稳定阶段持续接收增量命令流。

4. 为什么复制延迟会出现

由于复制是异步的,所以从库可能会短暂落后于主库,常见原因包括:

  • 主库写入量突然增大;
  • 网络带宽不足或网络抖动;
  • 从库本身 CPU、磁盘、内存压力过大;
  • 大 key、大批量写入导致复制流处理变慢。

这也是为什么很多关键一致性场景不能简单依赖“写主读从立刻可见”。


七、主从复制与持久化之间的关系

主从复制和持久化经常一起出现,但它们解决的问题不一样:

  • 主从复制:解决副本分发与高可用基础能力;
  • RDB / AOF 持久化:解决实例重启后的数据恢复。

不过在全量同步时,它们又会发生关联。

1. 全量同步通常依赖 RDB 快照

主库在给从库做全量同步时,通常会生成 RDB 快照并发送出去。

2. 持久化配置会影响复制成本

如果主库本身已经存在较高的持久化压力,再叠加全量复制,就更容易出现:

  • fork 阻塞放大;
  • Copy-On-Write 内存压力上升;
  • 磁盘和网络占用变高。

3. 复制不是备份

这一点非常重要。

如果主库误删数据、执行错误命令、业务写入脏数据,这些变化也会同步到从库。因此:

主从复制提高的是可用性,不是“历史可回滚能力”。

真正的备份仍然要依赖快照备份、AOF、异地备份等方案。


八、常用配置项与示例

下面给出一组比较常见的主从复制配置示例:

# 从库配置主库地址
replicaof 192.168.10.10 6379

# 主库认证信息
masteruser default
masterauth your_master_password

# 复制积压缓冲区
repl-backlog-size 256mb

# 从库只读
replica-read-only yes

# 与主库的心跳检测周期
repl-ping-replica-period 10

# 复制超时时间
repl-timeout 60

# 无盘复制,可按场景评估开启
repl-diskless-sync yes
repl-diskless-sync-delay 5

1. replica-read-only

从库默认只读,这样可以避免业务误写从库造成混乱。

2. repl-backlog-size

这个值太小,网络稍一抖动就可能触发全量同步;太大则会占用额外内存。要结合:

  • 主库写入速率;
  • 可接受断链时长;
  • 实例内存规模。

3. repl-diskless-sync

这是无盘复制能力。开启后,主库可以在某些场景下直接通过 socket 把 RDB 流发送给从库,减少磁盘中转。

是否开启要看环境特点:

  • 如果磁盘 IO 紧张,可能更有价值;
  • 如果网络稳定性一般,也要评估传输风险与运维习惯。

九、日常观察主从状态的常用命令

1. INFO replication

这是排查复制问题最常用的命令:

127.0.0.1:6379> INFO replication

在主库上,你通常能看到:

  • 当前角色是 master
  • 已连接从库数量;
  • 每个从库的 offset、lag;
  • backlog 状态。

在从库上,你通常能看到:

  • 当前角色是 slave / replica
  • 主库地址;
  • 主从连接状态;
  • 当前复制 offset;
  • 是否正在同步。

2. ROLE

127.0.0.1:6379> ROLE

这个命令可以快速看实例当前是主库、从库还是哨兵模式下的其他角色信息。

3. REPLICAOF

REPLICAOF 192.168.10.10 6379
REPLICAOF NO ONE

用于动态建立或解除主从关系。

4. WAIT

127.0.0.1:6379> SET payment:100 ok
OK
127.0.0.1:6379> WAIT 1 100
(integer) 1

WAIT 可以让客户端在写入后等待指定数量副本确认,用来在一定程度上增强写后复制可见性。但要注意:

  • 它不是强一致事务;
  • 只是让客户端在返回前等待更多复制确认;
  • 仍然要结合业务容忍度使用。

十、一个典型主从复制实验流程

如果你想自己验证,可以按下面思路搭一个最小实验。

1. 启动主库与从库

  • 主库:6379
  • 从库:6380

从库配置:

port 6380
replicaof 127.0.0.1 6379

2. 在主库写入数据

127.0.0.1:6379> SET site:name redis-demo
OK
127.0.0.1:6379> LPUSH jobs task1 task2
(integer) 2

3. 在从库读取验证

127.0.0.1:6380> GET site:name
"redis-demo"
127.0.0.1:6380> LRANGE jobs 0 -1
1) "task2"
2) "task1"

4. 查看复制状态

127.0.0.1:6380> INFO replication

重点关注:

  • master_link_status 是否为 up
  • master_sync_in_progress 是否为 0
  • slave_repl_offset 是否持续推进。

十一、生产中常见问题

1. 为什么主从复制不是强一致?

因为 Redis 复制默认是异步的。主库先处理写请求并返回客户端,然后再异步把命令传播到从库。

所以会出现:

  • 主库已写成功;
  • 从库短时间还没同步到;
  • 如果此时主库故障,可能发生最近一小段写入丢失。

2. 为什么从库重连后有时增量同步,有时全量同步?

关键看两点:

  • 主库 backlog 中是否还保留它缺失的那段数据;
  • 复制 ID 是否还能对得上这段历史。

对得上就部分重同步;对不上就全量同步。

3. 主从复制延迟大,先看什么?

建议先看:

  • INFO replication 中的 offset 差距;
  • 主库写入量是否突然升高;
  • 网络带宽与延迟;
  • 是否存在大 key、大批量写、Lua 脚本;
  • 从库自身 CPU、内存、磁盘压力。

4. 为什么多个从库同时重连会拖垮主库?

因为可能触发多副本全量同步,带来:

  • fork 压力;
  • RDB 生成压力;
  • 大量网络传输;
  • 主库内存额外消耗。

所以生产里常常需要控制副本重建节奏,而不是同时批量拉起。

5. 从库能不能写?

默认不建议。虽然某些场景可以调整只读策略,但会引入数据一致性混乱,通常不作为常规用法。

6. 主库误删数据后,从库能不能救回来?

通常不能直接依赖从库,因为删除命令也会同步过去。要恢复历史状态,还是要看是否有 RDB、AOF 或其他备份。


十二、实践建议

1. 不要把从库当成“绝对实时”的读库

如果业务对刚写入的数据立刻可见非常敏感,就要谨慎使用读写分离。否则很容易踩到复制延迟问题。

2. 根据写入速率合理设置 backlog

写入越快,越需要更大的 repl-backlog-size,否则网络稍有闪断就容易退化成全量同步。

3. 大实例特别关注全量同步成本

实例越大,fork、RDB、网络传输的代价越高。大实例应尽量减少频繁全量复制。

4. 主从复制配合监控一起看

至少建议长期关注:

  • 主从 offset 差值;
  • 主从连接状态;
  • 全量同步次数;
  • backlog 命中率;
  • 复制延迟;
  • 网络吞吐与磁盘压力。

5. 认清复制的边界

主从复制提升的是可用性和扩展性,但不是强一致、不是版本回滚、也不是完整备份方案。


十三、小结

理解 Redis 主从复制,最重要的是抓住下面几件事:

  1. 首次复制通常是全量同步:主库生成 RDB,从库加载快照,再补增量命令。
  2. 网络闪断后优先尝试部分重同步,依赖 replication ID、offset 和 backlog。
  3. 稳定运行时,主库通过异步复制流持续把写命令传播给从库。
  4. 主从复制是高可用基础,但不是强一致,也不是备份。
  5. Sentinel 和 Cluster 的故障切换,本质上都建立在副本复制之上。

如果你后续继续学习 Redis 的 Sentinel 和 Cluster,会发现很多故障切换、延迟、一致性边界问题,归根到底都和这里讲的复制机制密切相关。


📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

Redis进阶4.4: 原子操作实践

下一篇

Redis 架构篇5.2:Sentinel 哨兵机制