news 2026/10/22 8:30:09

Redis主从复制原理与实战:从全量同步到增量复制的完整链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis主从复制原理与实战:从全量同步到增量复制的完整链路

Redis 的主从复制,几乎是每个后端开发在从单机走向多机架构时绕不开的第一道坎。很多人第一次接触它,是因为线上读请求把单实例压得喘不过气,想加几个从节点分担查询;也有人是为了做数据冗余,防止主节点磁盘挂掉之后数据全丢。但真正把主从复制跑起来之后,问题才刚开始:从节点数据怎么是空的?主从延迟为什么忽高忽低?主节点重启之后从节点为什么连不上了?这些坑我在实际项目里几乎踩了个遍。这篇就把 Redis 主从复制从建立连接到数据同步、从全量复制到增量复制的完整链路拆开讲清楚,顺带把那些文档里一笔带过、实际却最容易出问题的地方补上。不管你是刚接触 Redis 的新手,还是已经用过但从没深究过原理的老手,看完应该都能对这套机制有个通透的理解。

1. 为什么单机 Redis 迟早要面对复制这件事

1.1 单实例的三个硬伤

Redis 单实例跑起来确实爽,部署简单、没有协调开销、延迟低。但只要业务量稍微上来一点,三个问题就会同时冒头。

第一个是读性能瓶颈。Redis 是单线程处理命令的(这里说的是命令执行主线程),所有读写请求都排在一个队列里。当 QPS 涨到几万甚至十几万,单实例的 CPU 会先扛不住。注意,瓶颈往往不在网络,而在命令执行本身。这时候最直接的思路就是把读请求分散出去,写请求还是走主节点,读请求分给多个从节点。

第二个是数据可靠性。单实例的 Redis 数据全在内存里,虽然开了 RDB 和 AOF 能持久化到磁盘,但磁盘坏了、机器宕了,恢复期间服务就是不可用的。主从复制能让数据实时同步到另一台机器,主节点挂了从节点至少还有一份完整数据。

第三个是故障恢复能力。单实例挂了就是彻底挂了,没有任何回旋余地。有了从节点,配合哨兵或者集群模式,才能做到自动故障转移。

这三个需求指向同一个技术手段:主从复制。它的本质就是把一个 Redis 实例(主节点)的数据,持续地同步到一个或多个 Redis 实例(从节点)上。

1.2 主从复制到底解决了什么,又没解决什么

先把边界划清楚,免得用错地方。

主从复制解决的是:数据冗余备份、读请求水平扩展、为故障转移提供基础。

主从复制不解决的是:写性能扩展(写还是只能在主节点)、自动故障转移(这需要哨兵或集群)、数据一致性保证(它是异步复制,主节点写完就返回,不保证从节点已经收到)。

这里有个特别容易误解的点:很多人以为主从复制是强一致的,主节点写成功从节点就一定有了。不是的。Redis 主从复制默认是异步的,主节点执行完写命令,把命令发给从节点之后就返回客户端了,根本不等从节点确认。这意味着主节点刚写完就宕机,这条数据可能还没传到从节点,切换之后数据就丢了。理解这一点,后面很多"诡异"现象就都能解释了。

提示:如果你的业务对数据丢失零容忍,光靠主从复制是不够的,需要配合 WAIT 命令做同步等待,或者考虑其他方案。但 WAIT 会牺牲性能,要权衡。

2. 主从复制的建立:从节点是怎么"认主"的

2.1 三种建立复制关系的方式

让一个节点变成从节点,有三种常见做法,实际项目里各有适用场景。

第一种是配置文件方式。在从节点的 redis.conf 里加上一行replicaof <masterip> <masterport>(老版本叫slaveof)。这种方式适合固定拓扑,节点重启后会自动重新建立复制关系,生产环境最常用。

第二种是启动命令参数方式。启动时加--replicaof <masterip> <masterport>,效果和配置文件一样,适合临时测试或者容器化部署时通过环境变量注入。

第三种是运行时命令方式。连上从节点后执行REPLICAOF <masterip> <masterport>,动态建立复制关系。这种方式不用重启,适合临时调整拓扑。想断开复制关系就执行REPLICAOF NO ONE,从节点会升格为主节点,但注意它不会丢弃已经同步的数据。

# 运行时建立复制关系 redis-cli -h 192.168.1.10 -p 6379 127.0.0.1:6379> REPLICAOF 192.168.1.20 6379 OK # 查看复制状态 127.0.0.1:6379> INFO replication

2.2 建立连接时到底发生了什么

从节点执行 REPLICAOF 之后,并不是立刻就开始传数据,中间有一串握手动作。理解这个过程,排查问题时才知道卡在哪一步。

从节点会先跟主节点建立 TCP 连接,然后发送一个 PING 命令确认主节点能正常响应。主节点回复 PONG 之后,从节点发送 AUTH(如果主节点配了密码)做认证。认证通过,从节点发送自己的监听端口信息,主节点记录下这个从节点的地址。

接下来从节点会发送PSYNC命令,这是整个复制流程的核心。PSYNC 带上两个参数:一个是主节点的 runid(运行 ID),一个是复制偏移量 offset。如果是第一次连接,从节点不知道主节点 runid,就发PSYNC ? -1,表示"我不知道你是谁,也没有历史数据,请给我全量同步"。

主节点收到 PSYNC 后要做一个判断:这个从节点是要全量同步还是增量同步?判断依据就是 runid 和 offset 能不能对上。对不上就走全量,对得上就走增量。这个判断逻辑是后面第 4 节的重点。

注意:老版本 Redis(2.8 之前)用的是 SYNC 命令,只支持全量复制,每次断线重连都要全量传一遍,效率极低。现在用的都是 PSYNC,支持增量。如果你看到某些老资料还在讲 SYNC,知道那是历史即可。

2.3 一个容易被忽略的细节:从节点默认是只读的

从节点建立复制关系后,默认配置replica-read-only yes,也就是只读。这是合理的,因为从节点的数据是从主节点同步过来的,如果允许写,主从数据就会不一致。

但实际项目里有人会想往从节点写数据,比如做一些临时缓存。这时候要么改配置成可写(强烈不建议,会破坏一致性),要么就老老实实把写请求都发到主节点。

还有个坑:从节点只读是针对普通客户端连接的,主节点通过复制连接发过来的写命令不受这个限制。所以从节点的数据变化只有两个来源:主节点同步过来的,以及从节点自己内部的过期删除等操作。

3. 全量复制:第一次同步为什么要传一整个 RDB

3.1 全量复制的完整链路

从节点第一次连上主节点,或者断线太久无法增量同步时,走的就是全量复制。这个过程比想象中复杂,涉及多个步骤和一次 RDB 文件的传输。

主节点收到全量同步请求后,会执行BGSAVE在后台生成 RDB 文件。注意是后台生成,主节点不会阻塞,还能继续处理客户端请求。但这里有个关键点:BGSAVE 期间新来的写命令怎么办?如果不管,这些写命令就不会出现在 RDB 里,从节点就丢数据了。

Redis 的做法是在内存里维护一个replication buffer(复制缓冲区),BGSAVE 开始之后所有新的写命令都追加到这个缓冲区里。等 RDB 生成完,主节点先把 RDB 文件发给从节点,从节点清空自己的旧数据、加载 RDB。加载完之后,主节点再把 replication buffer 里积累的写命令发给从节点。这样从节点的数据就能追上主节点的最新状态。

整个链路可以概括成:

  1. 从节点发送PSYNC ? -1
  2. 主节点执行BGSAVE生成 RDB,同时把新写命令写入 replication buffer
  3. 主节点发送 RDB 文件给从节点
  4. 从节点清空旧数据,加载 RDB
  5. 主节点发送 replication buffer 中的写命令
  6. 之后进入命令传播阶段,主节点每收到一个写命令就实时发给从节点

3.2 全量复制的开销到底有多大

全量复制是"重"操作,开销体现在三个地方。

主节点的 fork 开销。BGSAVE 会 fork 一个子进程,fork 本身在数据量大时会有明显的耗时。虽然 Linux 的 fork 用了写时复制(Copy-On-Write),但 fork 那一刻要复制页表,内存越大页表越大,耗时越长。实测中,一个几十 GB 的实例 fork 一次可能要几百毫秒甚至更久,这期间主线程是阻塞的。

RDB 文件的磁盘和网络开销。RDB 要写到磁盘再读出来发送,如果磁盘 IO 慢,这一步就是瓶颈。网络传输更是大头,一个 10GB 的 RDB 在千兆网卡上光传输就要一分多钟。

从节点加载 RDB 的开销。从节点收到 RDB 后要先清空自己的数据(FLUSHALL),再加载。加载期间从节点是阻塞的,无法响应客户端请求。如果从节点还在对外提供读服务,这段时间读请求会全部失败或超时。

提示:全量复制期间从节点会阻塞,所以生产环境要避免在业务高峰期做全量同步。可以通过replicaof的时机控制,或者用repl-diskless-sync让主节点直接把 RDB 通过网络发给从节点,省掉磁盘这一步。

3.3 无盘复制:省掉磁盘这一步

Redis 2.8.18 之后支持无盘复制,配置repl-diskless-sync yes。开启后主节点不再把 RDB 写到磁盘,而是 fork 出子进程后直接通过网络把 RDB 数据发给从节点。

这个模式适合磁盘 IO 是瓶颈、但网络带宽充足的场景。不过它也有代价:主节点要等一个repl-diskless-sync-delay(默认 5 秒)的窗口,把这段时间内请求同步的从节点攒一起,然后一次性并发发送,避免每个从节点都 fork 一次。这个延迟意味着从节点建立复制会慢几秒,但能显著减少主节点的 fork 次数。

选择有盘还是无盘,核心看瓶颈在哪。磁盘快网络慢,用有盘;磁盘慢网络快,用无盘。没有绝对优劣。

4. 增量复制:断线重连后怎么只补差的那部分

4.1 PSYNC 的判断逻辑

增量复制是 PSYNC 相比老 SYNC 最大的进步。它的核心是让从节点断线重连后,只同步断开期间丢失的那部分数据,而不是全量重来。

判断能不能增量,靠三个东西:runid、offset、repl_backlog。

runid 是每个 Redis 实例启动时生成的唯一标识。从节点会记住主节点的 runid。如果从节点重连时发现主节点的 runid 变了,说明主节点重启过(或者换了实例),那就没法增量,只能全量。

offset 是复制偏移量。主节点每发送 N 字节数据,自己的 offset 就加 N;从节点每收到 N 字节,自己的 offset 也加 N。正常情况下两者应该一致。从节点重连时会带上自己的 offset,主节点看这个 offset 之后的数据还在不在自己的 backlog 里。

repl_backlog 是主节点维护的一个环形缓冲区,默认 1MB,配置项repl-backlog-size。主节点发送给从节点的命令,除了发出去,还会写一份到这个 backlog 里。如果从节点的 offset 落在 backlog 范围内,主节点就能把 offset 之后的数据补发给它,走增量;如果 offset 太旧,已经被 backlog 覆盖了,那就只能全量。

4.2 repl_backlog 的大小怎么定

backlog 太小是增量复制失败最常见的原因。默认 1MB 在高写入场景下可能几秒钟就被写满了,从节点只要断线超过这个时间,重连就得全量。

backlog 该设多大?有个粗略的估算公式:

backlog_size >= 平均写入速率(字节/秒) × 预计最长断线时间(秒)

比如你的主节点平均每秒写入 500KB,你希望从节点断线 60 秒内都能增量恢复,那 backlog 至少要 500KB × 60 = 30MB。实际配置时再留点余量,设成 64MB 比较稳妥。

# 动态调整 backlog 大小(重启后失效,要写进配置文件) redis-cli CONFIG SET repl-backlog-size 64mb

注意:backlog 是所有从节点共享的,不是每个从节点一份。所以从节点越多、写入越频繁,backlog 被覆盖得越快。高写入 + 多从节点的场景,backlog 要设得更大。

4.3 增量复制的数据流

增量复制的过程相对简单:

  1. 从节点重连,发送PSYNC <runid> <offset>
  2. 主节点检查 runid 是否匹配、offset 是否在 backlog 范围内
  3. 都满足,主节点回复+CONTINUE,表示可以增量
  4. 主节点把 backlog 中 offset 之后的数据发给从节点
  5. 之后进入正常的命令传播阶段

如果 runid 不匹配或 offset 太旧,主节点回复+FULLRESYNC <runid> <offset>,从节点就知道要走全量了。

这里有个细节:主节点回复+CONTINUE之后,会立刻把 backlog 里 offset 之后的数据发过去,然后转入实时命令传播。从节点收到这些数据后更新自己的 offset,之后就跟主节点保持同步了。

5. 命令传播:同步建立之后主从怎么保持同步

5.1 实时传播的机制

全量或增量同步完成后,主从进入稳定状态,也就是命令传播阶段。这个阶段主节点每执行一个写命令,就会把这个命令(准确说是命令的协议格式)发给所有从节点。

注意,主节点是把写命令本身发给从节点,而不是发数据快照。从节点收到命令后自己执行一遍。这样做的好处是传输量小,一个SET key value命令就几十字节,比传整个 value 的数据结构高效得多。

但这也带来一个要求:从节点执行命令的结果必须和主节点一致。所以 Redis 要求主从的版本尽量一致,某些命令在不同版本上行为可能不同。另外,像SPOP、SRANDMEMBER这种带随机性的命令,主节点会把结果确定化之后再传播,保证从节点执行结果一致。

5.2 心跳检测:怎么知道对方还活着

命令传播是单向的(主到从),但主从之间还需要双向的心跳来确认对方状态。

从节点会每秒向主节点发送REPLCONF ACK <offset>,带上自己当前的复制偏移量。这个心跳有三个作用:一是让主节点知道从节点还活着;二是让主节点知道从节点同步到哪了;三是用于min-replicas-to-write这类配置的判断。

主节点也会定期(默认每 10 秒)向从节点发送 PING,确认从节点响应正常。如果从节点长时间没响应,主节点会把它标记为下线。

# 在主节点查看从节点状态 127.0.0.1:6379> INFO replication # 输出里会列出每个从节点的 ip、port、state、offset、lag 等信息

其中lag字段很关键,它表示从节点最后一次向主节点发送 ACK 距今多少秒。lag 持续大于 1 秒,说明从节点可能有问题或者网络有延迟。

5.3 主从延迟是怎么产生的

主从延迟是异步复制的必然产物,但延迟忽大忽小就有问题了。常见的延迟来源有几个。

网络往返。主节点发命令到从节点收到,中间有网络传输时间。同机房通常 1ms 以内,跨机房可能几十毫秒。

从节点负载。从节点如果还在对外提供读服务,读请求会占用 CPU,导致复制命令的处理被推迟。从节点越忙,延迟越大。

大命令阻塞。如果主节点执行了一个DEL大 key 或者FLUSHALL,这个命令传到从节点后,从节点执行也要花时间,期间无法处理其他复制命令,延迟就上来了。

主节点写入过快。主节点写入速率超过从节点处理速率,命令在从节点的接收缓冲区里堆积,延迟持续增长。

排查延迟,先看INFO replication里的 offset 差值,再看从节点的 CPU 和网络。如果 offset 差持续扩大,基本就是从节点处理不过来。

6. 那些文档不会告诉你的踩坑实录

6.1 从节点数据是空的:全量复制没走完

第一次配主从,最容易遇到的现象是:从节点INFO replication显示master_link_status:down,或者master_link_status:up但keyspace是空的。

这种情况八成是全量复制没走完。可能的原因:RDB 文件太大传输超时、从节点磁盘空间不够加载失败、主节点 BGSAVE 失败(比如stop-writes-on-bgsave-error触发)。

排查顺序:先看主节点日志有没有 BGSAVE 失败,再看从节点日志有没有加载 RDB 报错,最后看网络传输是否正常。我遇到过一次是从节点磁盘满了,RDB 下载到一半失败,日志里只有一行不起眼的错误,找了半天。

6.2 主节点重启后从节点疯狂全量同步

主节点重启后 runid 会变(除非配置了repl-diskless-load或者用了持久化的 runid),从节点发现 runid 对不上,就会触发全量同步。如果从节点多、数据量大,主节点重启后会被一堆全量同步请求打爆。

缓解办法:主节点重启前先做一次SAVE或者确保 RDB 是最新的,重启后从节点全量同步时能快一点。更好的办法是用哨兵或集群做故障转移,避免主节点直接重启。

6.3 从节点只读但数据还是不一致

理论上从节点只读,数据应该和主节点完全一致。但实际中可能因为过期键的处理出现短暂不一致。

Redis 的过期键删除有两种:惰性删除(访问时检查)和定期删除。从节点不会主动删除过期键,而是等主节点删除后同步DEL命令过来才删。所以在主节点删除之前,从节点上这个键可能已经逻辑过期但还存在。客户端在从节点读到这个键,会拿到过期数据。

这是设计上的取舍,不是 bug。如果业务对过期时间敏感,读请求最好还是走主节点,或者接受这个短暂的不一致。

6.4 复制缓冲区溢出导致连接断开

主节点的 replication buffer 是有限的(受client-output-buffer-limit replica控制,默认 256MB 硬限制、64MB 软限制持续 60 秒)。如果从节点处理太慢,buffer 堆积超过限制,主节点会强制断开这个从节点的连接。

断开之后从节点重连,如果 backlog 够大还能增量,否则又是全量。所以从节点性能要跟上,buffer 限制也要根据实际情况调整。

# 调整复制缓冲区限制:硬限制 512MB,软限制 128MB 持续 60 秒 redis-cli CONFIG SET client-output-buffer-limit "replica 512mb 128mb 60"

7. 主从复制的参数调优与监控要点

7.1 关键配置项一览

配置项默认值作用调优建议
replicaof无指定主节点生产用配置文件方式
replica-read-onlyyes从节点只读保持 yes,别改
repl-backlog-size1mb复制积压缓冲区大小按写入速率和断线容忍时间估算,建议 64mb 起
repl-backlog-ttl3600backlog 无从节点后保留时间从节点频繁断连可调大
repl-diskless-syncno无盘复制磁盘慢网络快时开启
repl-diskless-sync-delay5无盘复制等待窗口从节点多可适当调大
client-output-buffer-limit replica256mb 64mb 60复制缓冲区限制从节点慢时调大
min-replicas-to-write0至少几个从节点在线才允许写对可靠性要求高时设为 1 或 2
min-replicas-max-lag10从节点最大延迟秒数配合上一项使用

7.2 必须监控的指标

主从复制出问题往往不是突然的,而是有征兆。几个必须盯的指标:

master_link_status。up 表示连接正常,down 表示断开。这个要配告警。

master_repl_offset 与 slave_repl_offset 的差值。差值持续扩大说明从节点跟不上,延迟在累积。

master_last_io_seconds_ago。从节点最后一次和主节点通信距今秒数,超过 10 秒要警惕。

master_sync_in_progress。是否正在全量同步,为 1 说明正在同步,此时从节点不可用。

# 一条命令快速检查复制健康度 redis-cli INFO replication | grep -E "role|master_link_status|master_last_io_seconds_ago|master_sync_in_progress|slave_repl_offset"

7.3 一个实用的健康检查脚本思路

生产环境建议写个定时脚本,定期检查主从状态,异常时告警。核心逻辑:

  1. 连上每个从节点,读INFO replication
  2. 检查master_link_status是否为 up
  3. 检查master_last_io_seconds_ago是否小于阈值(比如 10 秒)
  4. 检查master_sync_in_progress是否为 0
  5. 连上主节点,对比主从 offset 差值是否在可接受范围
  6. 任一异常就触发告警

这个脚本用 Python 的 redis 库几行就能写出来,比等到业务报错再排查主动得多。

8. 从主从复制到高可用:下一步该往哪走

主从复制本身不提供自动故障转移。主节点挂了,从节点不会自动顶上,需要人工介入或者借助外部组件。

哨兵模式是在主从复制基础上加了一层监控和自动切换。哨兵集群监控主从节点,主节点挂了之后自动选一个从节点升为主节点,并通知其他从节点切换过去。适合中小规模、对自动故障转移有要求的场景。

集群模式则是分片 + 复制的组合,每个分片内部还是主从复制,分片之间做数据分片。适合数据量大、需要水平扩展的场景。

不管往哪个方向走,主从复制都是基础。把这一层理解透了,哨兵的选主逻辑、集群的故障转移,本质上都是在这套复制机制上做文章。

我在实际项目里的体会是:主从复制配置本身不难,难的是把它跑稳。backlog 大小、缓冲区限制、从节点性能,这几个参数要根据实际写入量和拓扑反复调。上线前一定要做断线重连测试和主节点重启测试,看看增量复制能不能正常工作,别等到真出故障了才发现每次都要全量同步。另外,监控比配置更重要,主从延迟这种问题,早发现早处理,拖到业务感知就晚了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 8:30:01

本地搭建Hadoop+Hive+Spark大数据环境:版本选型、配置实操与踩坑指南

1. 为什么我要在本地搭这套大数据环境大数据这套东西&#xff0c;很多人第一次接触都是在公司的测试集群上&#xff0c;点几下就能跑&#xff0c;但真到自己想复现一个完整链路的时候&#xff0c;往往卡在环境搭建这一步。我这次动手的起因很简单&#xff1a;手头有个数据处理的…

作者头像 李华
网站建设 2026/10/22 8:29:40

Flutter深度链接与Firebase身份验证:实现无密码登录闭环

我在做某个跨平台项目改版的时候&#xff0c;接了一个需求&#xff1a;用户在邮件里点一个链接&#xff0c;如果手机上装了 App&#xff0c;就直接打开 App 并且完成登录&#xff0c;不需要再输入密码&#xff1b;如果没装 App&#xff0c;就落到网页上引导下载。听起来不复杂&…

作者头像 李华
网站建设 2026/10/22 8:28:55

自动驾驶多任务视觉模型:YOLOP与OmniDet的设计与落地实践

做智驾感知这几年&#xff0c;视觉多任务模型&#xff08;也就是让检测、分割、车道线识别等任务共用一个主干网络、各自保留输出head&#xff09;几乎成了绕不开的话题。我最早接触这个概念是在某个智能驾驶项目里&#xff0c;当时车载平台算力有限&#xff0c;一个模型跑N个单…

作者头像 李华
网站建设 2026/10/22 8:28:10

OpenCV多通道图像混合实战:从BGR通道拆分到掩膜与HSV融合

1. 通道基础&#xff1a;看懂一张图的BGR与RGB内幕 1.1 一张彩色图的内存真相 不管你用OpenCV读图&#xff0c;还是用手机拍照片&#xff0c;一张彩色图像在计算机里从来都不是"一整块模糊的颜色"&#xff0c;而是被拆成三个独立的单色图层&#xff0c;再叠加出来的…

作者头像 李华
网站建设 2026/10/22 8:27:49

eMMC CMD6详解:EXT_CSD读写、HS400切换与分区控制实践

刚开始接触eMMC驱动的时候&#xff0c;我一直觉得CMD6就是一条“切换命令”&#xff1a;发送出去&#xff0c;设备换个模式&#xff0c;完事。直到后来调HS400切换&#xff0c;板子要么死在半路、要么时钟上去后疯狂报CRC错误&#xff0c;我才意识到&#xff0c;CMD6根本没有表…

作者头像 李华
网站建设 2026/10/22 8:27:38

订单系统实战:一日速通TypeScript类型建模与业务落地

我最初接触 TypeScript 的时候&#xff0c;其实挺抗拒的&#xff0c;总觉得“多写类型”是浪费时间&#xff0c;光看文档又枯燥得要命&#xff0c;学了三天还在纠结 interface 和 type 到底哪个更“高级”。直到后来用订单管理系统做了一次完整的实战&#xff0c;才真正把 …

作者头像 李华