暂无图片
暂无图片
3
暂无图片
暂无图片
暂无图片

Redis复制机制介绍

原创 chirpyli 2025-10-20
554

Redis复制

redis复制分为全量复制和增量复制。

全量复制过程:主节点启动后台保存进程以生成RDB文件。同时开始缓冲所有接收到的新写入命令。当后台保存完成时,主节点将数据库文件传输到从节点,从节点将其保存到磁盘,然后加载到内存中。主节点然后将所有缓冲的命令发送到从节点。这以命令流的形式完成,格式与Redis协议本身相同。

image

主从复制

配置主从复制的三种方式:

  • 方式一:配置主从的命令replicaof <masterip> <masterport>,可以在从节点redis.conf配置文件中进行配置
  • 方式二:使用redis客户端(redis-cli)在从节点通过命令replicaof <masterip> <masterport>进行配置。
  • 方式三: 从节点启动时指定主节点redis-server redis.conf replicaof <masterip> <masterport>。

示例:

127.0.0.1:6381> replicaof 127.0.0.1 6380  # 配置从节点
OK
127.0.0.1:6381> info replication   # 查看从节点信息
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
# ...

关闭复制:从节点可执行replicaof no one关闭复制,该节点将从从节点转为主节点,不会丢弃已复制的数据。。

replicaof命令使用时需要注意,如果当前节点已经是从节点,执行replicaof <masterip> <masterport>将停止与旧主节点的复制,并开始与指定新主节点的复制,同时会丢弃旧数据集。

主从复制工作机制:

  • 当主节点和从节点连接良好时,主节点通过向从节点发送命令流来同步从节点。
  • 当主节点和从节点连接断开时,从节点会重新连接并尝试进行部分同步,它将尝试仅获取断开连接期间遗漏的命令流部分。
  • 当无法进行部分同步时,从节点将请求全量同步。

redis默认使用异步复制,redis从节点周期性地向主节点异步确认它们接收到的数据量。客户端可以使用WAIT命令请求特定数据的同步复制。

WAIT命令

WAIT numreplicas timeout 此命令会阻塞当前客户端,直到之前的所有写命令成功传输并至少numereplicas个从节点成功接收到。如果达到了timeout参数指定的值(ms),即使未达到指定数量的副本,命令也不会返回。超时设置为0意味着永久阻塞。

WAIT并不能使redis成为一个强一致性存储。主要提升了在Sentinel或Redis Cluster故障转移场景下的数据安全性。使用WAIT命令后,可以确保在当前连接中,WAIT命令之前的所有写操作,都已经被其返回数量所代表的从节点功能接收。

配置详解

replicaof <masterip> <masterport> replicaof指令让一个redis实例成为另一个redis实例的从节点。

    1. redis复制是异步的,但可以配置主节点在与指定数量的从节点断开连接时停止接受写入。
    1. 如果复制连接中断时间较短,从节点可以执行部分重同步。应根据需求配置复制积压缓冲区大小。
    1. 复制过程自动进行,网络分区后从节点会自动重连主节点并重新同步

masterauth <password> 主节点有密码保护时需要配置,需要在开始复制前进行认证
masteruser <username> 如果默认用户不具备PSYNC等复制所需权限,需要配置专用用户。当从节点与主节点连接中断或正在同步时,有两种表现模式:

  • 当replica-serve-stale-data被设置为yes(默认值)时,继续响应客户端请求,可能返回过期数据,如果是首次同步,数据集可能为空。
  • 当replica-serve-stale-data被设置为no时,对除指定命令外(INFO, REPLICAOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, HOST and LATENCY)的所有请求返回"SYNC with master in progress"错误。

repl-diskless-sync no 全量同步策略,disk(磁盘) or socket。

  • 磁盘模式:主节点创建子进程生成RDB文件,随后主节点将RDB文件发送给从节点。
  • 无盘模式:主节点直接通过socket发送RDB到从节点,不落盘。无盘复制可配置等待时间以支持从多节点并行传输。

repl-diskless-sync-delay 5 无盘复制延迟时间,为了让多个从节点几乎同时请求全量同步时,能够批量同步,减少重复fork和RDB生成的次数,共享同一个RDB快照传输过程,从而提高效率。工作流程:第一个从节点请求全量同步,主节点不立即开始,而是等待repl-diskless-sync-delay秒,在这段延迟期间,其他从节点也可以连接上来并请求同步。延迟结束后,主节点生成一个RDB快照,然后通过网络同时发送给所有在这段延迟期间内排队等待的从节点。如果设置为0,则无盘同步将立即开始。建议在故障恢复后等场景可以配置较高的值(例如5~10秒),而其他场景(动态扩容等)可以适当减少等待时间。

repl-diskless-load disabled 从节点加载RDB的方式(无盘加载为实验性功能,谨慎使用,无盘加载可能会导致failover时数据丢失)。

  • disabled: 先存盘再加载
  • on-empty-db:仅在安全时无盘加载
  • swapdb: 内存保留当前数据副本。
    无盘加载:从节点可直接从socket中加载RDB,而无序将RDB落盘后再加载。

repl-ping-replica-period 10 从节点ping主节点间隔,默认值10秒,用于确保连接是活的。

repl-timeout 60 定义了以下三种场景的超时阈值:

  • 从节点视角,全量同步SYNC的批量传输超时:从节点执行全量同步时,主节点生成RDB文件并通过网络传输给从节点,若传输时间超过repl-timeout,则从节点会判定同步失败,从节点放弃当前同步,重新尝试全量同步。
  • 从节点视角,主节点数据和心跳检测超时:从节点在正常复制期间,若超过repl-timeout未收到主节点的数据或心跳,则认为主节点不可用。从节点断开与主节点的连接,并尝试重新建立连接。
  • 主节点视角:从节点心跳响应超时。主节点通过REPLCONF ACK定期检查从节点存活状态。若从节点超过repl-timeout未响应,主节点会判定从节点不可用,主节点断开与从节点的连接。
    repl-timeout必须大于repl-ping-replica-period的值。在实际场景中,该值不宜过小,因为网络是最不稳定的。

repl-disable-tcp-nodelay no 禁用从节点socket的TCP_NODELAY(默认no,低延迟优先;yes可减少带宽但增加40ms延迟)

复制积压缓冲区配置(影响部分重同步能力),当从节点断开时,主节点会将复制数据保存在backlog缓冲区中,当从节点重新连接时,不必重新复制全量数据,仅复制丢失的部分复制数据即可。
repl-backlog-size 1mb # 缓冲区大小(至少一个从节点连接时分配)

// 创建复制积压缓冲区,并不需要持久化存储,仅在内存中,影响部分重同步能力,即使丢失,仍可通过全量同步恢复 void createReplicationBacklog(void) { serverAssert(server.repl_backlog == NULL); // 确保当前没有复制积压缓冲区存在 server.repl_backlog = zmalloc(server.repl_backlog_size); server.repl_backlog_histlen = 0; server.repl_backlog_idx = 0; /* We don't have any data inside our buffer, but virtually the first * byte we have is the next byte that will be generated for the * replication stream. */ server.repl_backlog_off = server.master_repl_offset+1; }

repl-backlog-ttl 3600 # 所有从节点断开后保留缓冲区的时长(秒),0表示永不释放backlog。

从节点优先级(Sentinel选主依据,0表示不能升主,默认100)
replica-priority 100

replica-announced yes 控制该节点是否被Sentinel发现并纳入监控范围。设为no时,该节点不会出现在sentinel replicas <master>命令的结果中。Sentinel不会感知到这个节点的存在。即使设置为no,仍能参与故障转移,有可能被提升为主节点。如果需要彻底禁止某个副本成为主节点,必须同时将其replica-priority设置为0。可用于隐藏某些用于备份、测试或跨地域延迟较高的从节点,使其不参与常规的服务发现,但仍保持数据同步。

min-replicas-to-write 3 要求的最小在线副本数量。当连接的有效从节点数量少于N个,且这些副本的延迟时间都小于或等于M秒时,主节点可以停止接受写请求。这N个从节点必须处于“在线”状态。延迟秒数的计算基于从节点收到的最后一次ping,该延迟必须小于或等于指定值。该选项并不能保证写操作被N个从节点接收,但在可用从节点不足时,可以将数据丢失的风险窗口限制在指定的秒数范围内。例如要求至少3个延迟在10秒内的从节点。当设置为0时关闭此功能,此时主节点没有从节点也可以正常写入。
min-replicas-max-lag 10 允许的最大复制延迟(秒)。

replica-announce-ip 5.5.5.5 | replica-announce-port 1234 在复杂网络环境下(如NAT、Docker、云平台)确保主从复制正确识别的关键配置。自动检测在NAT/端口转发环境下,从节点自动检测的本地IP和端口可能时内部网络地址,对主节点或其他外部服务不可达。对此可以用手动声明机制。replica-announce-ip:从节点向主节点声明自己的外部可访问IP地址;replica-announce-port:从节点向主节点声明自己的外部可访问端口。这两个参数在从节点进行配置。

redis复制原理

每个Redis主实例都有一个复制ID:它是一个大的伪随机字符串,标记给定数据集的历史记录。每个主实例还有一个偏移量,每生成一个字节的复制流(用于发送给从节点,以修改数据集的新更改更新从节点的状态)就递增。即使没有实际连接的从节点,复制偏移量也会递增,因此基本上每对给定的Replication ID, offset(复制ID和偏移量)识别主实例数据集的精确版本。

void changeReplicationId(void) { getRandomHexChars(server.replid,CONFIG_RUN_ID_SIZE); server.replid[CONFIG_RUN_ID_SIZE] = '\0'; }

当从节点连接到主节点时,使用PSYNC命令发送它们旧主节点复制ID和到目前为止已处理的偏移量。这样主节点就可以发送所需的增量部分。但是如果主实例缓冲区中没有足够的积压,或者从节点引用的历史纪录(复制ID)不再已知,就会发生全量同步,这种情况下,从节点将从头开始获取数据集的完整数据。

复制ID基本上标记了数据的给定历史记录。每次一个实例作为主节点从头开始重启时,或一个从节点被提升为主节点时,都会为此节点生成了一个新的复制ID。连接到主节点的从节点在握手后将继承其复制ID。因此,具有相同的ID的两个实例通过它们持有相同数据但可能在不同时间的事实相关联。正是偏移量起着逻辑时间的作用,用于理解在给定历史记录(复制ID)下,谁持有最新的数据集。

例如,如果两个节点A和B具有相同的复制ID,但一个的偏移量为1000,另一个为1023,这意味着第一个节点缺少应用于数据集的某些命令。这也意味着A通过应用少量命令即可达到与B完全相同的状态。

redis实例有两个复制ID的原因是因为从节点被提升为主节点。故障转移后,被提升的副本需要仍然记住它过去的复制ID,因为该复制ID是旧主实例的ID。这样,当其他从节点与新主节点同步时,它们将尝试使用旧主节点复制ID进行部分同步。这将按预期工作,因为当从节点被提升为主节点时,它将其辅助ID设置为旧主节点的复制ID,记住此ID切换发生时的偏移量。之后它将选择一个新的随机复制ID,因为新的历史纪录开始了。处理新连接的从节点时,主节点将使用当前ID和辅助ID来匹配它们的ID和偏移量。简而言之,这意味这故障转移后,连接到新提升的主实例的副本无需执行完整同步。

void shiftReplicationId(void) { memcpy(server.replid2,server.replid,sizeof(server.replid)); /* We set the second replid offset to the master offset + 1, since * the slave will ask for the first byte it has not yet received, so * we need to add one to the offset: for example if, as a slave, we are * sure we have the same history as the master for 50 bytes, after we * are turned into a master, we can accept a PSYNC request with offset * 51, since the slave asking has the same history up to the 50th * byte, and is asking for the new bytes starting at offset 51. */ server.second_replid_offset = server.master_repl_offset+1; changeReplicationId(); serverLog(LL_WARNING,"Setting secondary replication ID to %s, valid up to offset: %lld. New replication ID is %s", server.replid2, server.second_replid_offset, server.replid); }

为什么被提升为主节点的从节点需要在故障转移后更改其复制ID?存在一种可能,旧主实例由于网络分区仍在作为主节点工作,保留相同的复制ID将违反“任意两个随机实例的相同ID和相同偏移量意味着它们具有相同数据集”这一事实。

一主一从节点的状态:

127.0.0.1:6379> info replication # Replication role:master # 主节点 connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=14,lag=0 master_failover_state:no-failover master_replid:f0c92deb38de697d6647b3457773b811b6196573 # 复制ID master_replid2:0000000000000000000000000000000000000000 master_repl_offset:14 # 偏移量 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 # 复制积压缓冲区大小 repl_backlog_first_byte_offset:1 # 复制积压缓冲区起始偏移量 repl_backlog_histlen:14 # 复制积压缓冲区中数据的长度 127.0.0.1:6380> info replication # Replication role:slave # 从节点 master_host:127.0.0.1 master_port:6379 master_link_status:up # 主节点连接状态,up表示连接正常 master_last_io_seconds_ago:2 master_sync_in_progress:0 # 是否正在同步 slave_read_repl_offset:56 # 从节点读取复制偏移量 slave_repl_offset:56 # 从节点复制偏移量 slave_priority:100 # 故障转移优先级 slave_read_only:1 replica_announced:1 # 副本被Sentinel宣告 connected_slaves:0 master_failover_state:no-failover master_replid:f0c92deb38de697d6647b3457773b811b6196573 # 从节点的复制ID和主节点相同 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:56 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:56

关闭主节点,将从节点提升为主节点

127.0.0.1:6380> replicaof no one # 将从节点提升为主节点 OK 127.0.0.1:6380> info replication # Replication role:master connected_slaves:0 master_failover_state:no-failover master_replid:84a5cb4c551897e90f38cef233939ede49950ff7 # 新的主节点的复制ID master_replid2:f0c92deb38de697d6647b3457773b811b6196573 # 旧的主节点的复制ID master_repl_offset:1568 second_repl_offset:1569 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:1568

参考文档:
Redis replication
Redis 复制
Redis复制

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论