☰
Redis主从同步核心机制:从全量复制到高可用架构
2026/10/10 10:51:39 网站建设 项目流程

Redis 主从同步,是我这几年用 Redis 过程中觉得最值得讲透的一个机制。很多人会把主从复制当成“备份”,其实它更核心的价值是让 Redis 从“单点工具”变成“可水平扩展的基础设施”。单机 Redis 再快,也扛不住两类场景:一是宕机后所有请求直接打挂,二是读流量一上来,单节点 CPU 和网卡瞬间成为瓶颈。主从同步就是解决这两个问题的第一步,也是后面理解哨兵、集群的基石。这篇文章我会从零开始,把主从同步的原理、配置、常见坑一次讲清楚,适合刚学完 Redis 基础命令、想进一步理解高可用架构的读者,也适合已经在用 Redis 但没系统梳理过复制机制的开发者。

1. 为什么需要主从同步:单机 Redis 的三个天花板

1.1 可用性缺口:单点宕机等于业务停摆

先聊一个最现实的问题:Redis 的所有读写都发生在一个进程里,这个进程挂了,整个缓存层就没了。有人会说,Redis 不是有 RDB 和 AOF 持久化吗?重启恢复不就行了?但恢复是有代价的,如果内存里放着 10G 数据,重启后从 RDB 加载可能就要几十秒,AOF 重放更是可能分钟级。这几十秒到几分钟里,所有缓存请求全部穿透到数据库,数据库大概率直接被打垮。

主从同步解决的就是这个“空窗期”。你准备一台从节点,让它实时同步主节点的数据,主节点真出问题了,从节点下一秒就能顶上。虽然这时候需要手动切换或配合哨兵,但数据已经有一份热备,恢复时间从分钟级压缩到秒级。别小看这个差别,实际生产环境里,数据库能多扛几十秒还是几秒钟,直接决定了你这个事故是 P2 还是 P1。

1.2 读写分布不均:一个主节点扛不住全部读流量

第二个场景在电商、内容类的服务里特别常见。一个商品的详情页,写操作可能一天就几千次,但读操作一个小时就能到几十万次。如果把所有读请求都压在主节点上,主节点既要处理写命令,又要处理读命令,网卡和 CPU 迟早被打满。

主从同步天然支持“一主多从”的架构。写操作打到主节点,读操作分散到多个从节点,每个从节点都有一份完整的数据副本,读能力可以近乎线性扩展。我见过一个模拟项目,单机 Redis 读 QPS 大概能扛 8 万左右,加了三台从节点做读写分离后,读 QPS 直接冲到 22 万,主节点负载反而降了不少。这就是主从复制最直观的价值。

1.3 数据安全:单机持久化文件救不了硬件故障

RDB 和 AOF 能防“进程崩溃”,但防不了“机器损坏”和“磁盘故障”。我实际处理过一次磁盘损坏的事故,主节点所在的机器硬盘直接报废,RDB 文件跟着一起没了。这时候如果有一个从节点在另一台机器上,数据至少还在,不至于从零开始重建缓存。

另外,RDB 持久化本身是耗资源的操作,fork 子进程时如果数据量很大,主线程会有明显的卡顿。有了从节点之后,完全可以在从节点上开启 RDB 持久化,把主节点的持久化压力分担掉,主节点专心服务读写请求。这是很多团队实际在用的方案,效果非常明显。

主从同步解决的核心问题就是这三个:高可用、读写分离、数据冗余。它是 Redis 从单机走向分布式架构的第一步,也是最容易理解的一步。接下来我用实际配置和抓包日志,把这个过程完整拆给大家看。

2. 从零搭建一主两从:配置与验证全流程

2.1 实例规划:三个 Redis 进程模拟最小集群

先不要想得太复杂,实际生产环境里主从节点可能是三台不同的物理机,但在本地学习或做 Demo 时,完全可以用三个不同端口的 Redis 实例模拟。假设我规划的架构是这样:

节点IP / 端口角色用途
节点A127.0.0.1:6379主节点处理写请求
节点B127.0.0.1:6380从节点1处理读请求
节点C127.0.0.1:6381从节点2处理读请求 / 备份节点

这里我用的是同一台机器三个端口,主要是为了演示方便。生产环境请务必把主从节点部署在不同的机器上,不然一台物理机挂了,三个节点一起消失,主从复制等于白搭。这是很多人第一次搭主从时容易忽略的点。

2.2 两种建立主从关系的方式

我先把两种方式都写出来,因为实际工作中两种都会遇到。第一种是用配置文件,redis.conf 里添加下面这行:

replicaof 127.0.0.1 6379

这是 Redis 5.0 之后推荐的写法。在 5.0 之前,这个配置项叫 slaveof,虽然现在 Redis 7.x 为了兼容还能识别 slaveof,但新项目里建议全部用 replicaof。加了这行配置后,从节点启动时会自动向主节点发起同步请求,之后每次启动都是自动建立主从关系,不需要人为干预。

第二种方式是命令动态修改,不需要重启进程:

REPLICAOF 127.0.0.1 6379

我比较推荐在临时调整架构时用命令方式,比如某个从节点想临时切换复制源,一条命令就能搞定,不用改配置文件再重启。但要注意,命令方式指定的主从关系不会持久化,重启之后从节点会回到自己配置文件里的状态,没有配置就变回独立节点。如果想保留,还是得把 replicaof 写进配置文件。

2.3 验证主从是否就绪

配置完成后,在主节点上执行:

INFO replication

正常情况下主节点会输出类似这样的信息:

# Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6380,state=online,offset=1694,lag=0 slave1:ip=127.0.0.1,port=6381,state=online,offset=1694,lag=0

重点关注几个字段:state 必须是 online,offset 是两个从节点的复制偏移量,lag 是主从之间的延迟秒数。在从节点上执行同样的命令,role 应该显示 slave 或者 replica,并且能看到 master_link_status:up。

快速验证数据是否真的同步过去了,可以在主节点写入一个 key,然后在从节点直接 GET 一下。能查到就说明链路跑通了。我这里再说一个技巧:主节点写入一个随机 key 后,如果从节点上查不到,不要急着怀疑同步出了问题,先确认一下你查的是不是从节点的 6380/6381 端口,这类低级错误我见得太多了。

3. 主从同步的核心机制:全量复制与部分复制

3.1 全量重同步:第一次建立从属关系的完整流程

当从节点第一次连接主节点,或者从节点断开太久无法增量恢复时,会触发全量重同步。这个过程我拆成四步:

第一步,从节点向主节点发送 PSYNC 命令,携带自己的复制 ID 和偏移量。如果从节点是全新节点,复制 ID 是 0,偏移量是 -1,表示“我什么都没有,给我全部数据”。

第二步,主节点收到 PSYNC 后,判断需要全量同步,于是开始生成 RDB 快照。这个 RDB 生成过程是异步的,主节点不会阻塞写请求。但注意,如果主节点内存数据量很大,fork 生成子进程时可能短暂卡顿,所以生产环境要么给主节点预留足够的 CPU 资源,要么干脆在从节点上做 RDB。

第三步,RDB 文件生成完毕后,主节点把文件发送给从节点。从节点收到后,先把 RDB 加载到内存,这个过程中从节点会拒绝所有外部请求,所以全量同步期间从节点是“不可用”的。

第四步,RDB 开始生成之后,主节点收到的所有写命令都会被记录在复制积压缓冲区里。RDB 发送完成后,主节点会把缓冲区里的命令继续同步给从节点,确保从节点最终和主节点在同一个状态。

这里有个容易忽略的点:全量同步期间从节点的旧数据会被清掉。如果从节点之前已经有一批数据,全量同步一触发,旧数据直接被 RDB 替换。我遇到过有人想在从节点上保留一些自定义数据,结果一同步全没了,所以有这种需求就得自己想别的方案,主从复制不会帮你保留。

3.2 部分重同步:断线重连如何避免全量复制

全量重同步虽然能解决问题,但代价太大,尤其是大实例上,几 GB 的 RDB 传输会占用大量带宽,从节点加载期间还会拒绝服务。最理想的情况是,从节点只断开了一小会儿,期间主节点产生的写命令不多,重连后只补发这部分就行。这就是部分重同步要干的事。

部分重同步依赖三个核心要素:

  • 主节点的 run_id:主节点每次启动都会生成一个唯一的运行 ID,从节点首次连接时会记住。
  • 主从各自的复制偏移量 offset:每传输一个字节的命令流,offset 就递增一次,这个偏移量是判断“落后多少”的依据。
  • 复制积压缓冲区:主节点维护的一块固定大小的环形内存,里面保存着最近一段时间的写命令。

当从节点断线重连后,会发送 PSYNC 带上自己的 offset。主节点拿到这个 offset,如果发现它仍然在自己的复制积压缓冲区范围内,就只把缓冲区里 offset 之后的数据发给从节点,完成增量补发。如果从节点落后的太多,offset 已经不在缓冲区范围内,那没办法,只能退回全量重同步。

复制积压缓冲区的大小直接决定了“能从短时断线中恢复”的容错空间。默认值是 1MB,说实话太小了。我建议生产环境按这个公式估算一下:

缓冲区大小 = 预估主节点每秒写入字节数 × 可接受的最长断线时间

举个例子,主节点平均写速率是 2MB/s,业务允许从节点断线 60 秒内都能增量恢复,那缓冲区至少要设到 120MB。配置项是:

repl-backlog-size 128mb

3.3 复制 ID、偏移量与积压缓冲区怎么协同工作

这里我把三个概念放到一起讲清楚,因为它们之间的关系比较微妙,很多人学了半天还是一团浆糊。

复制 ID 是主节点的“身份标识”,每次主节点启动都会重新生成。从节点第一次全量同步的时候,会把主节点的复制 ID 记成自己的复制 ID,后续断线重连时发 PSYNC,带上这个 ID 和 offset,主节点就能认出“这是我之前那个从节点”。

如果主节点重启了,复制 ID 会变。这时候从节点带着旧的复制 ID 再来,主节点发现自己不认识这个 ID,会强制从节点做全量重同步。这就引出一个实际排查经验:如果发现从节点频繁做全量复制,先看看主节点是不是经常重启,或者主节点的复制积压缓冲区是不是挪到了新生成的临时 ID 上。

偏移量背后还有一个完整的机制:主节点每次向从节点发送写命令时,自己也会维护一个 master_repl_offset;从节点每接收一条命令,自己的 offset 也会更新。所以排查主从是否一致,最直接的办法就是从 INFO replication 里对比主节点的 master_repl_offset 和从节点的 slave_repl_offset。两者差值越大,说明从节点落后越多。

3.4 无盘复制与复制参数调优

Redis 2.8.18 引入了无盘复制功能,主节点不把 RDB 写入磁盘,而是直接在内存中生成 RDB 并同步发送给从节点。这个选项对磁盘性能差的环境非常有用。默认情况下,RDB 要先落盘再发送,如果磁盘 IO 很慢,全量同步的耗时会被拉长。配置项这样设置:

repl-diskless-sync yes repl-diskless-sync-delay 5

其中 repl-diskless-sync-delay 是指主节点生成 RDB 后等多长时间再发给从节点,目的是让多个同时请求全量同步的从节点可以共享同一个 RDB 文件,避免每个从节点都独立生成一份,浪费 CPU。默认 5 秒,按需调整。

另一个值得关注的参数是 replica-serve-stale-data。默认情况下,从节点和主节点断线期间,还是会继续响应读请求,哪怕读到的可能是过期数据。如果你做的是强一致性场景,比如库存查询,建议把它关掉:

replica-serve-stale-data no

这样断线期间的从节点会直接拒绝请求,宁可不返回数据,也不返回错误数据。这个参数很多人在一致性出问题时根本没想到,值得记住。

4. 实操中遇到的坑:主从同步最容易翻车的几个场景

4.1 主节点持久化没开,从节点一起跟着变“空”

这是我在实际运维里遇到最典型的坑。有人为了让主节点性能更好,把 save 配置和 appendonly 都关掉了,然后搭了主从。平时看起来一切正常,主节点写数据,从节点同步,数据都在。结果某天主节点宕机,从节点被提升为新的主节点。最关键的是,旧主节点重启后由于没有持久化文件,数据是空的,而它重启后还会以新主节点的从节点身份出现,把满数据的从节点直接覆盖成空数据。

这个事故的本质是:主从复制只负责“实时同步”,不负责“持久化兜底”。如果主节点不开持久化,数据从崩溃那一刻起就已经不安全了。我的建议是:主节点必须开启 AOF 或至少 RDB,从节点可以关持久化或只开 RDB,绝不能两边都裸奔。这个坑踩一次就够了,代价很大。

4.2 从节点偷偷被写入数据,主从一直触发全量复制

默认情况下从节点是只读的,配置文件里 replica-read-only yes 是默认开启的。但有一种情况,有人会把它改成 no,比如想在从节点上临时做一些本地统计,就会往从节点写一些 key。这些 key 在主节点上不存在,一旦触发主从重新同步,从节点上这些额外的 key 就会被清掉,而且如果写入的 key 和主节点上的 key 冲突,从节点会一直报错,复制链路会反复断连。

处理这种问题的标准做法是:不要改 replica-read-only,从节点上严禁写入。如果有在从节点做计算和统计的需求,可以用 Redis 的客户端读数据后在应用层处理,或者专门准备一个独立的实例来跑任务,别拿从节点当沙盒用。

4.3 过期 key 在主从上的行为不一致

这是一个隐藏比较深的细节。Redis 删除过期 key 有两种方式:惰性删除和主动定期删除。在主从架构中,主节点负责生成删除命令,然后同步给从节点。从节点不会自己独立去删除过期 key,必须等主节点发 DEL 命令过来。

这个机制带来的问题是:主节点上已经过期但还没被清理的 key,从节点上依然能读到。因为从节点判断一个 key 是否过期时,如果还没收到主节点的删除指令,它是不会主动删的。这在缓存一致性要求高的场景里很容易出问题。

我的建议是,如果业务对“过期后立即读不到”有强要求,最好在应用层加一层过期时间的判断。Redis 从 7.0 开始主从之间对过期 key 处理有了改进,但底层“从节点不主动删”的逻辑本质上没有变,还是别完全依赖它。

4.4 网络抖动引发频繁全量复制

从节点和主节点之间的网络如果有轻微抖动,物理链路会短暂断开。如果断线时间比较短,offset 还在缓冲区范围内,重连后能增量恢复。但如果网络一直在“通一下、断一下”的状态,每次断线重连时从节点都发起 PSYNC,就可能出现一种情况:从节点每次恢复一点点,又断掉,又恢复一点点,反而触发不了全量同步,但 offset 一直追不上。

这种问题最典型的症状是:主节点 INFO 里看到从节点状态是 online,但是 lag 一直不为 0,或者 slave_repl_offset 和 master_repl_offset 的差值在缓慢增加。排查步骤是先看系统日志,确认有没有网卡丢包,再看内核参数 net.ipv4.tcp_keepalive_time 是否设置得过长。实际处理时,我会先把主从节点之间的网络延迟调到 5ms 以内,然后把复制积压缓冲区调大,给增量恢复留足空间。

5. 常见问题排查与速查表

5.1 主从复制问题速查表

问题现象可能原因排查命令与解决思路
从节点 state 为 down网络不通 / 主节点未开启ping 主节点 IP,检查防火墙,确认主节点在运行
从节点一直全量重同步复制积压缓冲区太小 / 从节点落后太多调大 repl-backlog-size,检查网络稳定性
主从数据不一致主从延迟 / 从节点可写入了数据看 lag 字段,关闭从节点写权限,检查链路带宽
从节点加载 RDB 很慢实例内存大 / 磁盘 IO 慢调大 maxmemory 持久化策略,或换 SSD
主节点重启后从节点全量同步主节点 run_id 变化尽量让主节点不要频繁重启,或用哨兵切换
主从之间 offset 无法对齐网络抖动 / 命令阻塞查看 Redis 慢日志,分析主节点是否存在阻塞操作

这张表是我总结出来的高频问题,但实际场景往往更复杂,同一个现象可能是多个原因叠加。排查的原则永远是先看状态、再看日志、最后才动配置,不要一上来就改参数。

5.2 一次同步中断后的全量复制排查实录

我分享一个真实的排查过程,场景是一个模拟电商项目里的 Redis 主从。某天从节点的 INFO 里显示 master_link_status:down,同步完全停掉。我的排查顺序是这样的:

第一步,先看系统日志,发现从节点和主节点之间有大量 TCP 重传,说明物理链路确实有问题。当时网络工程师反馈是交换机端口抖动,属于基础设施故障。

第二步,网络恢复后,从节点自动重连,但触发了全量重同步,而不是增量恢复。我看了下主节点的配置,repl-backlog-size 还是默认的 1MB。主节点在断线期间积累了超过 1MB 的写命令,offset 已经超出缓冲区,所以只能全量同步。这是我之前提过的参数没调到位造成的。

第三步,我重新评估了主节点的写速率,加了监控后发现高峰期每秒写入约 3MB,按 5 分钟断线时间容忍度,把 repl-backlog-size 改成了 1GB。改完之后,后来一次同样时长的网络抖动,从节点重连后恢复增量同步,全程只用了不到 1 秒。

这个案例的教训是:缓冲区的设置一定要结合业务写速率来评估,不能用了默认值就以为万事大吉。复制积压缓冲区本质上是“时间换空间”,调大了能容忍更长时间的断线,但内存占用也会上升,需要业务上做取舍。

6. 主从复制之外:生产环境一定要了解的延伸点

6.1 读写分离的隐藏成本

很多人做完主从就急着把所有读请求都分发到从节点,但没意识到一个问题:主从之间的同步是异步的,主节点写了一条数据,从节点可能落后零点几毫秒。如果业务场景是“写入之后立即读取”,比如用户下单后立刻查订单详情,请求可能被路由到还没同步完成的从节点上,结果查不到。

这种问题的解决方案有很多:第一种是写入后强制读主节点,也就是“写后读一致性”套路;第二种是设置一个极短的延迟容忍窗口,比如 Redis 主从延迟监控表里 lag 超过 50ms 的从节点就把流量摘掉;第三种是给数据加版本号,应用层判断过期时间。没有完美方案,只有适合业务的方案。

6.2 从节点数量不是越多越好

一主多从能扛读流量,但主节点向所有从节点同步数据也是要开销的。每增加一个从节点,主节点就要额外维护一份复制流,fork 生成 RDB 时压力也更大。当从节点数量超过 5 个时,我建议采用“主 → 从 → 从”的链式复制结构,也就是让一部分从节点去同步另一部分从节点,减轻主节点的复制压力。

链式复制的配置很简单,就是让中间层从节点既做从,又配置为下一级节点的“主”。但要注意,链式复制会增大末端从节点的延迟,如果对读新鲜度要求很高,层级不要超过两层。

6.3 哨兵是主从复制的自动化升级

手动做主从切换不是长久之计。主节点宕机时,从节点虽然数据在,但不会自己转正,必须有人去执行 REPLICAOF NO ONE 命令。哨兵就是干这个的:它持续监控主节点的健康状态,一旦发现主节点失联,在多数派哨兵同意的情况下,选取一个从节点提升为新主节点,并把其他从节点的复制源自动切换到新主上。

所以我的建议是,生产环境里主从复制必须配合哨兵使用,绝不能裸奔。主从复制是“数据冗余”的底座,哨兵是“自动切换”的开关,两者结合才是高可用的完整体。

实测跑了这么久,我发现主从复制这个知识点,光看文档是学不透的,一定要自己搭一次环境、断一次网、刷一批数据,亲眼看一下从节点状态怎么变化,offset 怎么增长,全量同步和增量同步分别在什么时机触发。踩过这些坑之后,再去看哨兵和集群,你会觉得整个 Redis 高可用体系的逻辑一下就通透了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询