Skip to content

Redis 主从复制

本文介绍Redis主从复制相关原理、配置及问题。

1. 概念

Redis的主从复制是指将主节点(master)的数据同步到从节点(replica),从而实现读写分离、高可用、容灾备份等功能。

Redis的主从复制不依赖主节点的RDB或AOF配置,即主节点的RDB或AOF持久化可以关闭。

2. 工作原理

2.1 概念解释

Redis的主从复制分为两种方式:

  • 全量复制:主节点生成一个 RDB 文件(快照)并发送给从节点,从节点接收到RDB文件后,先清空自身的数据库,然后加载RDB文件;
  • 增量同步:当全量复制开始时,主节点会将写命令缓存到一个缓冲区(repl-backlog),同步完成后将这些增量命令发送给从节点,从节点通过 repl-backlog(复制缓冲区)接收主节点的写命令流,异步执行这些命令以保持数据一致性;

Redis如何实现全量复制和增量同步?通过Replication ID 和 Offset。

  • Replication ID :是一个唯一标识符,用于标识主节点的复制流(replication stream),或者说主节点数据集的版本号。它是一个 40 字节的随机字符串,通常由主节点在启动或成为主节点时生成。
  • Offset:偏移量,是一个递增的数值,表示主节点或从节点在复制流中的当前位置。它记录了主节点当前缓冲区中的写命令字节数,或者从节点已接收并应用的字节数。

我们可以使用 INFO REPLICATION 命令可以查看主节点和从节点的 Replication ID 和 Offset。

image-20250609172036416

在上面的结果中,我们主要关注三个值:

  • master_replid:即Replication ID,表示该节点数据集版本;
  • repl_backlog_first_byte_offset:该节点缓冲区repl_backlog中存储有效命令的起始位置;
  • master_repl_offset:该节点缓冲区repl_backlog中存储有效命令的结束位置;

可以通过以下图理解repl_backlog_first_byte_offsetmaster_repl_offset

image-20250609173501270

2.2 工作流程

  • 首先主节点启动,生成Replication ID

  • 然后开始接收来自客户端的写命令,并将其存入repl backlog,并更新master_repl_offset;

  • 当repl backlog写满后,开始覆盖旧命令,并更新repl_backlog_first_byte_offsetmaster_repl_offset,可以理解为repl backlog buffer在写指令序列上往后移动,总是保留最新的写指令;

  • 然后从节点启动,连接主节点;

  • 当从节点连接主节点后,发送PSYNC命令,包含从节点的Replication IDOffset,如果是第一次,则Replication ID值为?Offset值为-1

    txt
    PSYNC ? -1
  • 主节点接收到从节点发送过来的Replication IDOffset

    • 首先对比从节点和主节点的Replication ID,如果不同,则执行全量复制流程;
    • 如果相同,则对比从节点的Offset和主节点的repl_backlog_first_byte_offset
      • 如果从节点的Offset < 主节点的repl_backlog_first_byte_offset,表示从节点落后太多,主节点的repl backlog缓冲区中没有从节点缺失的命令,需要执行全量复制流程;
      • 如果从节点的Offset >= 主节点的repl_backlog_first_byte_offset,则执行增量同步流程;
  • 主节点返回:

    • 全量复制,返回内容以+FULLRESYNC打头,格式为:

      txt
      +FULLRESYNC <replication_id> <offset>
      $<length>
      后面是 RDB 数据流
    • 增量同步,返回内容以+CONTINUE打头,格式为:

      txt
      +CONTINUE
      
      RESP格式写命令
  • 当进行增量同步流程时,由主节点将写命令发送给从节点,并且主节点会定时ping从节点;

2.3 演示

下面我们用telnet手动发送 PSYNC命令,来演示主从复制。

如果在MacOS系统下没有telnet,可以使用homebrew安装:

brew install telnet

首先启动一个Redis实例,然后依次执行以下命令:

txt
INFO replication
set k6 v6
INFO replication

可以看到master_repl_offset持续增长,说明repl backlog buffer中存储的命令不断增多:

image-20250609203212862

然后,在命令行中使用telnet连接Redis:

bash
telnet 127.0.0.1 6379

第一次发送PSYNC ? -1命令,表示首次连接到主节点,会执行全量复制:

image-20250609203723137

可以看到返回了全量复制。

之后,我们在另一个Redis客户端执行写操作命令:

txt
set k7 v7

可以看到,命令行界面会输出这条写命令,说明全量复制后,增量同步自动开始:

image-20250609204140244

接下来我们演示增量同步:

image-20250609204357713

image-20250609204504261

3. 配置与命令

本小节介绍如何使用配置或命令开启主从复制。

3.1 配置

配置分为主节点配置和从节点配置。

主节点配置如下

  • daemonize yes:设置Redis 服务器在启动时以“守护进程”(daemon)的方式在后台运行;

  • bind * -::* :设置任何IPv4或IPv6地址可访问Redis服务;

  • protected-mode no:关闭保护模式

    保护模式(protected mode)是 Redis 为防止误操作和安全风险而设计的一种机制。当保护模式开启(protected-mode yes,默认值)时,如果 Redis 没有设置密码,并且监听的是非本地地址(如 0.0.0.0 或 *),Redis 只允许本地连接,拒绝外部主机的访问。这可以防止 Redis 实例被意外暴露在公网时被未授权用户访问和利用。

  • port 6379:指定端口;

  • dir /data/:指定工作目录;

  • logfile "":指定日志文件路径;

  • requirepass 123456:指定访问Redis的密码;

  • 开启RDB相关配置;

  • 开启AOF相关配置;

从节点配置如下

  • masterauth <master-password>:配置从节点访问主节点所需要的密码,即主节点Redis密码;
  • replicaof <masterip> <masterport>:配置主节点Redis地址;

3.1 命令

关于主从复制的命令有如下两条:

  • REPLICAOF <host port>:在运行时设置主节点的地址,从而使从节点可以改变跟随的主节点;

  • REPLICAOF <NO ONE>:取消该从节点的从属关系,使之成为主节点;

  • CONFIG SET parameter value [parameter value ...]:如果主节点需要密码访问,可以动态使用CONFIG命令更改配置项,配置主节点密码:

    txt
    CONFIG SET masterauth 123456
  • INFO replication:查看主从复制信息;

4. 测试案例

以下测试案例基于Docker配置。

4.1 配置:一主一从

在Docker 中,Redis主节点正常启动,监听在6379端口上,映射到宿主机6379端口。

查看Redis主节点所在主机IP地址:

image-20250610103646653

配置从节点:

txt
replicaof 172.17.0.2 6379
masterauth 123456

启动Redis从节点,同样监听在6379端口,映射到宿主机6380端口。

image-20250610103844403

可以看到复制成功,并且不能在从节点上执行写操作。

在从节点上查看主从复制信息:

image-20250610104552758

在主节点上查看主从复制信息:

image-20250610104643858

4.2 命令:二主一从

我们先启动两个Redis主节点,然后让一个从节点先使用配置文件跟随一个主节点,再在运行时使用命令跟随另一个主节点。

启动另一个主节点,同样监听在6379端口,映射到宿主机6389端口上,并且需要密码abcdef才能访问。

启动成功后,先设置一些数据:

image-20250610104710574

在从节点(端口6380)上使用命令replicaof no one,使从节点成为主节点:

image-20250610104821133

这是从节点可以正常执行写操作:

image-20250610104921787

然后使用replicaof host port改变主节点:

image-20250610105250969

可以看到,从节点改变主节点,会先清空自己的数据。

4.3 测试主节点下线

主节点下线后,从节点仍然保持从节点地位不变:

image-20250610110744262

5. 问题

在Redis的主从复制场景中,需要注意以下问题:

  • 全量同步的性能开销:全量同步需要主节点生成 RDB 快照(或通过无磁盘复制传输数据流),并通过网络发送给从节点,这会消耗大量 CPU、内存和网络带宽。在数据量大或网络较慢的情况下,全量同步可能导致主节点性能下降或从节点同步时间过长,从而性能低或数据一致性问题。

  • 复制缓冲区(repl-backlog)溢出:repl-backlog 是一个固定大小的环形缓冲区(默认 1MB),用于存储主节点的写命令以支持部分同步。在高写负载或从节点长时间断连的情况下,缓冲区可能被新命令覆盖,导致从节点的 slave_repl_offset 小于缓冲区的起始 Offset,触发全量同步。可根据负载调整配置:

    txt
    repl-backlog-size 10mb
  • 可用性低:如果主节点意外下线,会导致整个主从复制拓扑结构失效,从节点无法上位成为主节点;

参考资料

[1] https://redis.io/docs/latest/operate/oss_and_stack/management/replication/

[2] https://www.bilibili.com/video/BV13R4y1v7sP