☰
Redis高可用三台阶:主从复制、哨兵与Cluster实战全解析
2026/10/22 3:57:06 网站建设 项目流程

后端同学对 Redis 的感情都一样:平时用着是真爽,一旦数据量上来、节点一挂,就瞬间从“缓存工具”变成“事故源头”。今天这篇不聊基础 API,直接对准 Redis 6 里最绕不开的三个集群级方案:主从复制、哨兵模式、Redis Cluster 集群。说实话,很多教程把它们当成三个独立话题讲,但实际工作中它们是同一条高可用演进线上的三个台阶:先让数据有备份,再让故障能自动切换,最后让容量可以横向扩展。搞懂这条线,你再去看任何系统的高可用架构,基本一通百通。

这篇文章适合两类人:一类是已经在生产环境用 Redis、但只用了单机模式,想搞清楚该怎么往高可用方向演进的开发者;另一类是准备从零搭建一套 Redis 集群,想知道每一步命令、每个参数为什么这么配的运维同学。我会把原理、配置、踩坑一次讲透,尽量让你照着就能在自己的环境里复现。

1. 先把问题摆清楚:单机 Redis 到底哪里不够用

很多人一上来就直奔“集群”两个字,但如果不先理解单机 Redis 的瓶颈,后面配置主从、哨兵、Cluster 时很容易只知其然不知其所以然。我习惯把单机 Redis 的瓶颈分成四个维度,任何一项逼近上限,都会逼着你往集群方向走。

1.1 单机 Redis 的四个天花板

第一个是读压力。Redis 单实例的读性能其实很高,每秒十几万次 QPS 并不罕见,但那是建立在命令足够简单、网络延迟足够低的前提上。一旦业务量上来,比如一个日活很高的业务系统,缓存命中的读取请求把单机 CPU 打满,你再怎么优化命令都救不回来,这时候需要多个从节点分担读请求。

第二个是写压力。单实例的单线程模型决定了写命令只能串行执行,虽然 Redis 命令本身够快,但写到一定程度就会触及 CPU 核心上限。尤其在高并发写操作、大量SET/LPUSH场景下,单节点很难持续扛住。

第三个是容量。单机内存是有限制的。一台物理机内存再大,也不可能无限加。当数据量超过单机内存承载能力,要么降低缓存命中率,要么淘汰大量 key,要么就得把数据分散到多台机器上。主从复制不解决容量问题,只有 Cluster 的数据分片才能解决。

第四个是可用性。单机 Redis 一旦宕机,缓存全部失效,流量直接打到底层数据库,轻则数据库告警,重则长时间不可用。Redis 本身不带宕机自愈能力,所以需要引入主从复制保留副本,再由哨兵负责自动切换。

1.2 三个方案解决的是不同问题

搞清楚了四个天花板,三条技术线的定位就很清晰了:

  • 主从复制:解决数据冗余和读扩展。主节点写,从节点同步副本,应用可以把读请求分发到从节点,但主节点宕机后不会自动切换。
  • 哨兵模式:在主从复制基础上增加监控和自动故障转移。它解决的是主节点宕机时,系统能不能自动选出一个新主节点继续服务。
  • Redis Cluster:通过数据分片解决容量和写扩展问题,同时内置高可用能力。数据被自动分散到多个主节点上,每个主节点又可以挂从节点,兼顾扩展性和可用性。

这里最容易被忽视的一点是:这三个方案不是互斥的,而是层层叠加的。Redis Cluster 的每个主节点背后仍然是一套主从复制关系,甚至你可以在某些场景下先用主从复制解决读压力,再引入哨兵解决自动切换。理解这个递进关系,后续选型才不会纠结。

2. 主从复制:所有高可用的地基

主从复制是哨兵和 Cluster 的基础。我见过不少人跳过这一步直接玩 Cluster,结果遇到同步延迟、复制断连、数据不一致问题,排查起来特别痛苦。所以这块值得花时间讲细一点。

2.1 全量与增量同步:Redis 6 的复制协议到底怎么跑

主从复制说白了就是让从节点实时拿到主节点的数据变更。 Redis 6 的复制协议核心命令是PSYNC,分为全量同步和增量同步两种情况。

第一次建立主从关系,或者主从之间的复制链路断开太久、无法增量续传时,会触发全量同步,整个过程经历这几个阶段:

  1. 从节点发PSYNC ? -1,表示“我没有历史数据,给我全量”。
  2. 主节点收到后执行BGSAVE,在后台生成 RDB 快照文件,同时把生成快照期间收到的所有写命令写入复制缓冲区repl_backlog。
  3. 主节点把 RDB 文件发给从节点,从节点先清空自己的旧数据,再加载 RDB。
  4. RDB 加载完成后,主节点把repl_backlog里的增量命令发给从节点。
  5. 后续所有写命令都会以命令流的方式实时同步给从节点。

这里最关键的是第 2 步里的repl_backlog。很多同学不理解:为什么全量同步之后主节点还要单独维护一个缓冲区?原因很简单,BGSAVE需要时间,RDB 传输也需要时间,从节点在加载 RDB 期间,主节点不可能停止服务,这期间产生的写命令必须有个地方暂存,否则从节点加载完 RDB 就丢了一批更新。

增量同步是断线重连后用的。从节点重新连接时发PSYNC <replid> <offset>,把自己复制进度告诉主节点。主节点检查这个 offset 是否还在repl_backlog缓冲区里:

  • 如果不在,就只能退化成全量同步,重新传 RDB。
  • 如果在,就把从节点落后的那部分命令直接补发过去。

所以repl_backlog的大小直接决定了你能容忍从节点断线多久。我一般用这个公式估算:

repl_backlog_size = 写入高峰期每秒命令字节数 × 期望容忍的断线重连时间(秒)

举个例子,某业务高峰期每秒写入 2MB 数据,你想让从节点断线 300 秒后重连还能增量续传,那么repl-backlog-size至少配 600MB。实际配置我通常会再放大 1.5 到 2 倍,因为网络抖动可能导致命令堆积比预期快得多。默认值只有 1MB,生产环境基本不够用。

2.2 从节点配置三步走与同步状态验证

搭建主从复制非常快,关键就一个配置项:

replicaof 10.0.0.20 6379

如果是临时调整,不修改配置文件,也可以用命令:

CONFIG SET replicaof 10.0.0.20 6379

在 Redis 5 之前这个参数叫slaveof,从 Redis 5 开始官方用replica取代了带有旧时代色彩的slave体系,Redis 6 里我们直接用replicaof就好。

除了replicaof,从节点还有几个参数值得关注:

参数作用我的建议
replica-read-only yes从节点只读保持默认,别让从节点被误写
repl-backlog-size 64mb复制缓冲区大小按业务写入量估算,不要用默认 1MB
replica-serve-stale-data yes同步未完成时是否服务旧数据看业务容忍度,一致性要求高就设 no
repl-disable-tcp-nodelay no是否延迟合并小包延迟敏感保持 no,节省带宽可设 yes

配置完成后,用INFO replication查看状态:

redis-cli -p 6380 INFO replication

一个正常的从节点输出类似:

role:slave master_host:10.0.0.20 master_port:6379 master_link_status:up slave_repl_offset:140875

重点关注master_link_status,如果是up,说明同步链路是通的。在主节点上执行同样的INFO replication,会看到connected_slaves:1,以及从节点的 IP、端口、offset和lag信息。offset两边的数值是否一致,是判断主从是否追平最重要的指标。

2.3 主从复制里容易踩的三个坑

主从复制看似简单,生产环境里的坑却不少。我挑三个最常遇到的讲。

坑一:从节点读到过期键。Redis 的过期策略在主节点是惰性删除加定期删除,从节点自己不会主动删 key,而是等主节点在 key 过期后发DEL命令来删。这导致一个现象:主节点上某个 key 已经逻辑过期,从节点如果还没收到删除命令,客户端可能还能读到旧值。Redis 6 里从节点在读取时其实已经会判断逻辑过期并返回空值,但主从之间始终存在一个时间差窗口。如果你的业务对过期数据特别敏感,需要自己评估这个窗口期。

坑二:maxmemory 不统一导致从节点被淘汰。如果主节点设置了maxmemory和淘汰策略,从节点也记得同步一致。如果从节点没设,或者策略不一致,数据量上来后两边可能各自淘汰不同的 key,最终导致主从数据不一致。

坑三:主节点宕机后,整个系统并没有自动恢复。这是主从复制最尴尬的地方:从节点虽然数据还在,但不会自动升级为主节点。业务只能人工干预,或者等主节点重启。一旦主节点短时间起不来,可用性就断了。这也是为什么真正的高可用方案必须引入哨兵。

3. 哨兵模式:让系统自动决定谁来当主

哨兵模式解决的痛点非常明确:主节点挂了,能不能自动选一个从节点顶上,等旧主恢复后再自动归队。我早期理解哨兵时总觉得很高深,后来跟团队里的运维同学聊过一次,才发现它本质就是一个独立运行的 Redis 监控进程,监控主节点和从节点的健康状态。

3.1 哨兵到底在干什么:三个定时任务解析

哨兵进程启动后,会周期性执行三个任务,很多人讲哨兵只讲“监控”,其实这三个任务协同起来才构成完整的自动故障转移机制。

第一个任务是每 10 秒向被监控的主节点和从节点发送INFO命令,拿到当前拓扑信息。这么做是为了动态感知主从关系的变化,比如某节点切换成了主节点、新的从节点加入等。第二个任务是每 2 秒向__sentinel__:hello频道发布一条消息,内容包含自己的地址和当前认识的主节点信息。所有哨兵都订阅这个频道,通过这种方式互相发现、互通情报。第三个任务是每 1 秒向所有监控的节点发送一次PING,用来判断目标节点是否存活。这个 1 秒的 PING 是判断主观下线的基础。

三个任务分别在 10 秒、2 秒、1 秒尺度上运行,涵盖了“感知拓扑变化、哨兵互相通信、节点健康检查”三个层面。理解了这套机制,后面看故障转移就容易多了。

3.2 哨兵配置关键参数与部署数量计算

哨兵本身也是一个 Redis 服务,可以单独起,也可以和业务节点混部,但生产环境我强烈建议独立部署。配置一个最简哨兵如下:

port 26379 sentinel monitor mymaster 10.0.0.20 6379 2 sentinel down-after-milliseconds 10000 sentinel failover-timeout 180000 sentinel parallel-syncs 1 sentinel auth-pass mymaster yourpassword

这里每个配置都有讲究:

  • sentinel monitor mymaster 10.0.0.20 6379 2:监控名为mymaster的主节点,2是 quorum,表示需要至少 2 个哨兵主观判断主节点不可用,才能确认客观下线并触发故障转移。
  • down-after-milliseconds 10000:单个哨兵连续 10 秒内 PING 不通目标节点,就判定为主观下线。
  • failover-timeout 180000:故障转移流程的超时时间,超过后会重试。
  • parallel-syncs 1:新主节点确定后,同时允许几个从节点重新同步数据。设 1 是为了避免多个从节点同时做全量复制,把主节点带宽打爆。

哨兵最少要起多少个?我的建议是奇数个,至少 3 个。为什么?因为哨兵要选出一个“老大”来执行故障转移,选举需要多数派同意。假如你只有 2 个哨兵,其中一个宕机,剩下那一个就凑不够多数派,故障转移根本不会执行。3 个哨兵可以容忍挂 1 个,5 个哨兵可以容忍挂 2 个。注意这里的 quorum 和多数派是两回事:quorum用于确认客观下线,而实际执行故障转移前需要获得哨兵节点总数一半以上的投票才能当选 leader。常见组合是 3 个哨兵配 quorum=2,5 个哨兵配 quorum=3。

3.3 故障转移全过程:从宕机到新主上线

我用一个具体场景把故障转移流程串一遍。假设有 1 个主节点、2 个从节点、3 个哨兵。主节点突然宕机,接下来会发生什么:

  1. 哨兵 A 每秒 PING 主节点连续超时,超过down-after-milliseconds后,哨兵 A 先在本地标记该主节点为主观下线。
  2. 哨兵 A 通过SENTINEL is-master-down-by-addr命令询问哨兵 B 和 C,问它们是否也认为主节点不可用。
  3. 收到 2 个确认后,达到 quorum=2 的要求,哨兵 A 判断该主节点为客观下线,同时发起 leader 选举,竞选“本次故障转移的执行者”。
  4. Leader 产生后,从现有的从节点列表中筛选新主节点。优先选replica-priority数值小的节点;优先级一样就选复制偏移量最大的那个,因为它数据最接近主节点;再一样的情况下比较 run_id,选较小的。
  5. Leader 向被选中的从节点发送REPLICAOF NO ONE,让它从只读从节点变成可写主节点。
  6. 然后向其他从节点发送REPLICAOF <新主IP> <新主端口>,让它们重新指向新主节点开始同步。
  7. 旧主节点恢复后会被哨兵自动降级为从节点,重新挂到新主节点下面。

整个过程对业务方是透明的,但应用不会自动感知。客户端之前一直连接旧主节点,切换后需要去哨兵获取新的主节点地址,这就是客户端要接哨兵地址而不是直连 Redis 主节点地址的原因。

3.4 客户端接入哨兵的核心逻辑

很多同学把哨兵配置好了,结果应用还是直连旧的 Redis 地址,主节点一切换应用立刻报错。正确的做法是应用连接哨兵进程,通过命令获取当前主节点信息:

SENTINEL get-master-addr-by-name mymaster

返回结果类似:

10.0.0.30 6379

拿到地址后应用再建立真正的 Redis 连接。另外,成熟的 Redis 客户端大多内置了哨兵支持,比如 Java 的 Lettuce 和 Jedis 都提供SentinelMode连接方式。它们不仅会启动时向哨兵获取主节点地址,还会订阅哨兵的+switch-master频道,一旦主节点发生切换,客户端能立刻收到消息并重建连接。所以使用哨兵模式时,应用层第一原则就是:不要直连主节点,要连哨兵。

4. Redis Cluster:数据分片与横向扩展

如果说主从复制和哨兵解决的是“高可用”,那 Redis Cluster 解决的核心问题是“扩展”。当数据量超过单机内存,或者写压力超过单节点能力时,Cluster 把数据自动拆成多个分片放到不同节点上,让整个集群的总容量和总吞吐量能随节点增加而提升。Redis 6 的 Cluster 已经非常成熟,支持了更完善的运维工具和 ACL。

4.1 16384 个哈希槽到底是怎么分配的

Redis Cluster 的数据分片单位是哈希槽(slot),整个集群固定有 16384 个槽。每个 key 属于哪个槽,由下面这个计算决定:

slot = CRC16(key) % 16384

CRC16 是 Redis 自己实现的一个哈希算法,计算完再对 16384 取模,得到一个 0 到 16383 之间的槽位。集群里的每个主节点负责一部分槽区间,比如一个三主集群可以这样分配:节点 A 负责 0-5460,节点 B 负责 5461-10922,节点 C 负责 10923-16383。客户端算出一个 key 的槽位后,根据槽位映射表就能知道该找哪个节点。

这里有个经典问题:为什么是 16384,而不是 65536?Redis 作者在社区里解释过:集群节点之间通过心跳包传播槽位信息,槽位越多,心跳包里的位图就越大,网络开销越高。16384 个槽对应 2KB 的位图,而 65536 个槽对应 8KB。考虑到一个集群的节点数通常不会超过 1000,16384 已经足够用了。

还需要注意多键操作的槽位限制。Cluster 模式下,一个命令涉及多个 key 时,这些 key 必须落在同一个哈希槽里,否则会报CROSSSLOT错误。解决方法是使用哈希标签(hash tag):哈希计算只在{}里的内容上进行。比如:

{user:1001}:name {user:1001}:age

这两个 key 的user:1001相同,所以会落到同一个槽,就可以放在一个事务或 Lua 脚本里操作了。

4.2 集群内部通信与 MOVED/ASK 重定向

Cluster 节点之间通过 gossip 协议互相通信,每 100 毫秒互相交换一次心跳,心跳里携带节点状态、槽位信息、集群配置等。这意味着每个节点都维护着整个集群的视图,知道每个槽由哪个节点负责。

客户端向某个节点发起请求时,节点会本地算出的 key 的槽。如果槽正好在自己手上,直接处理;如果不在,就会返回一个MOVED错误,告诉客户端正确的节点地址:

-MOVED 5676 10.0.0.31:7001

客户端收到MOVED后需要重新请求这个新地址。智能客户端会把槽位映射缓存下来,下次直接找到正确的节点。这就是为什么用 Cluster 模式时,不能再用普通的 Redis 客户端连接,而要用支持集群模式的客户端,比如redis-cli -c或者 Lettuce、JedisCluster。

除了MOVED,还有一个ASK重定向,这是在槽位迁移过程中出现的。假设槽 5676 正在从节点 A 迁往节点 B,A 上还剩一部分 key 没迁走,当请求访问一个恰好还没迁走的 key 时,A 返回ASK并给出目标节点 B 的地址。客户端收到ASK后需要先发一个ASKING命令给 B,再发送真正操作,否则 B 会拒绝不归属自己的 key。MOVED和ASK的区别可以这样记:MOVED表示槽的归属权已经永久变更,客户端要更新缓存;ASK只是迁移期间的临时转向,不能更新缓存。

4.3 完整搭建流程:从零到一个可用集群

实际操作中,直接用redis-cli --cluster create命令是最省事的。比如我有 6 个 Redis 实例,端口从 7000 到 7005,想搭一个 3 主 3 从的集群:

redis-cli --cluster create \ 10.0.0.21:7000 10.0.0.22:7001 10.0.0.23:7002 \ 10.0.0.24:7003 10.0.0.25:7004 10.0.0.26:7005 \ --cluster-replicas 1

每个实例的redis.conf需要提前开启集群模式:

port 7000 cluster-enabled yes cluster-config-file nodes-7000.conf cluster-node-timeout 15000 appendonly yes

cluster-config-file是当前节点保存集群状态的文件,由 Redis 自动维护,不需要手工编辑;cluster-node-timeout用来判断节点故障,类似哨兵里down-after-milliseconds的作用。

命令执行后,Redis 会自动完成三件事:节点之间互相握手、分配 16384 个槽、给每个主节点指定一个从节点。以 6 节点 3 主 3 从为例,最终会形成类似这样的拓扑:

  • 主节点 7000 负责 0-5460,从节点 7003 负责备份
  • 主节点 7001 负责 5461-10922,从节点 7004 负责备份
  • 主节点 7002 负责 10923-16383,从节点 7005 负责备份

登录验证一下:

redis-cli -c -p 7000 127.0.0.1:7000> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384

再检查节点视图:

127.0.0.1:7000> CLUSTER NODES

cluster_state:ok是最重要的指标,它要求 16384 个槽全部被覆盖,只要有槽没分配,集群就会进入失败状态。日常维护中你要时刻记住这一点:Cluster 是整体性的,槽不完整,整个集群都不干活。

4.4 扩容、缩容与槽迁移的实操要点

集群不是建好就完事了,业务增长后大概率要扩容。新增一个主节点很容易:

redis-cli --cluster add-node 10.0.0.27:7006 10.0.0.21:7000

但这时的 7006 节点是“空主节点”,手里一个槽都没有,不承担任何数据。真正把数据分给它,需要重新分片:

redis-cli --cluster reshard 10.0.0.21:7000

命令会进入交互模式,问你迁多少个槽、从哪些源节点迁到哪个目标节点。比如我想从原三个主节点各匀一些槽给 7006,总共迁 1000 个槽。程序会自动扫描槽里的 key,再用MIGRATE命令把这些 key 逐步搬到新节点。迁移过程中集群不需要停机,如果客户端恰好访问了一个正在迁移的 key,就会收到ASK重定向,由客户端自动处理。

缩容时反过来,先要把要下线的节点的槽迁走,再用下面的命令移除:

redis-cli --cluster del-node 10.0.0.21:7000 <node-id>

艾玛,这里必须强调一个顺序问题:先迁移槽,再删节点。如果你直接 del-node 一个还持有槽的节点,集群会立刻丢失槽位,进入CLUSTERDOWN状态,整个集群不可写。我亲眼见过有人缩容时图省事直接删节点,结果线上所有写请求全部报错,场面非常惨烈。

5. 三种方案选型:别在第一步就选错

堆完原理和实操,最后聊最现实的问题:项目里到底该用哪一种?这不是技术越高级越好,而是要匹配团队运维能力和业务阶段。我列了张对比表,基本覆盖选型时所有需要权衡的点。

维度主从复制哨兵模式Redis Cluster
数据分片无,单主写无,单主写有,多主写入
容量扩展不可扩不可扩可横向扩
读扩展可从节点分担读可从节点分担读数据分片后各自承担
故障自动转移无有有
客户端复杂度普通客户端直连需支持哨兵模式需支持 Cluster 模式
多键操作/Lua支持支持受哈希槽限制
运维复杂度低中较高
适用数据量单机可容纳单机可容纳单机内存不够用

我自己平时选型的建议很直接:

  • 单机内存还没到天花板,但怕节点宕机、希望自动切换,选哨兵模式。这是最均衡的高可用方案,数据量在几个 G 甚至几十 G,单节点都能扛的情况下,没必要上 Cluster。
  • 数据量明显超过单机内存,或者写并发单节点扛不住,选Redis Cluster。它可以动态扩容,把容量和压力分摊到多台机器上。但你要接受多键操作受限、运维复杂度上升这些代价。
  • 纯粹为了备份数据、降低主节点读压力,可以先用主从复制过渡。但长期看,只做主从不配哨兵等于裸奔,主节点一宕还是要人工介入。

记住一句话:哨兵解决的是“坏了怎么办”,Cluster 解决的是“装不下了怎么办”。这两个需求可能同时出现,所以我们在实际项目中,往往是先上 Cluster,然后在 Cluster 内部依靠每个主节点的从节点完成自动切换,而不是单独部署一套哨兵。

6. 高频问题与实战排查技巧

写了这么多配置和原理,真正到了生产环境,问题总是五花八门。这里整理一份我实战中见过的高频问题速查表,后面再补充几条独家排障思路。

现象可能原因排查方向
主从同步反复全量重传repl-backlog-size太小,从节点落后太多调大 backlog,检查网络抖动
从节点读取到过期数据主从删除有延迟窗口业务侧接受或主动规避
哨兵一直不触发切换quorum 设置过高,存活哨兵不够检查哨兵数、多数派选举条件
哨兵切换后客户端持续报错客户端直连旧主地址应用改为连接哨兵获取地址
集群写入返回 MOVED客户端不支持 Cluster 路由使用集群客户端或-c模式
集群写入返回 CLUSTERDOWN有槽未分配,或多数主节点不可用用CLUSTER INFO检查槽状态
扩容后数据分布不均槽迁移比例设置不当用reshard重新平衡
大 key 迁移阻塞节点单 key 过大导致MIGRATE耗时久先拆 key,或用限速迁移

6.1 主从同步反复断线怎么查

遇到主从不同步,我建议先看主节点和从节点各自的INFO replication,对比master_repl_offset和slave_repl_offset。差距持续变大,说明从节点追不上主节点。然后用info stats看total_net_input_bytes和sync_full计数器。如果sync_full数值不断上涨,说明全量同步太频繁,基本是 backlog 太小,从节点断线一会儿就追不回来了。处理方式就是调大repl-backlog-size,同时排查网络是不是有丢包。

还有一个隐蔽原因:主节点开启了repl-diskless-sync时,全量同步会直接在内存里生成 RDB 流。有次我遇到从节点一直同步失败,查到最后是磁盘型 RDB 生成时 I/O 占满,后来换成 diskless 才解决。如果从节点数量很少、网络带宽充足,repl-diskless-sync yes确实能提升同步速度。

6.2 Cluster 槽迁移时的几个细节

Cluster 迁移过程看起来很自动化,但实操里有几个细节必须知道。第一,reshard会逐个 key 迁移,如果存在超大 key,比如一个几 MB 甚至更大的 value,MIGRATE会阻塞一段时间。所以迁移前最好用redis-cli --bigkeys扫一遍,把大 key 提前处理掉。第二,迁移到一半时如果有写入请求,客户端会先收到ASK,再带上ASKING重发到目标节点,多数智能客户端会自动完成这个流程,但部分老版本客户端支持得不好,需要升级客户端版本。

第三,槽位迁移期间,如果目标节点还没完全接管槽,但源节点已经不再持有部分 key,可能短暂出现访问异常。整个迁移过程虽然不断服,但不代表完全无感,建议在业务低峰期操作。第四,缩容删除节点前,务必用redis-cli --cluster check确认目标节点槽已经清空,再执行del-node。

6.3 关于脑裂和数据丢失,一定要有的心理预期

Redis 主从复制默认是异步的,主节点写入成功后就向客户端返回成功,但这条写命令可能还没同步给从节点。如果这时候主节点宕机,哨兵或 Cluster 把某个从节点提升为新主,那么旧主上最近一小段写入就永远丢了。这是 Redis 高可用方案里一个无法绕开的短板。

官方提供了两个参数来缓解这个问题,也可以理解为“用可用性换一致性”:

min-replicas-to-write 1 min-replicas-max-lag 10

含义是:主节点在最近 10 秒内如果连一个从节点都没跟上,就拒绝写入。这样当主节点和所有从节点都失联时,新写入会被拒绝,而不是悄悄丢数据。代价很明显:网络抖动严重时,主节点会因为暂时联系不上从节点而拒绝写入,影响可用性。所以我一般只在一致性要求较高的核心业务上开启,而且要配合监控和告警。

另外多说一句,集群环境不要用FLUSHALL这种命令开玩笑。之前有个同事在 Cluster 的任意节点上执行FLUSHALL,虽然只清空了该节点负责的槽位,但因为槽位分布不均,业务侧瞬间像是“丢了一半数据”。任何在 Redis 集群上跑的清理类操作,先确认影响范围,再执行。

最后分享一点个人的工程体会

我早期做 Redis 高可用时,犯过一个特别典型的错误:先把主从复制搭好,然后手动写脚本去切换到从节点,脚本里各种判断节点存活状态、检查数据偏移量,代码写了一堆,还是经常出问题。后来换成了哨兵模式,发现这些逻辑官方早就做完了,而且做得比自己写的脚本健壮得多。从那以后我的原则就变成了:能复用 Redis 官方机制,就尽量不自己造轮子。

给新手的建议也很简单:先在测试环境完整跑一遍主从复制、哨兵故障切换、Cluster 缩扩容,尤其是把主节点直接kill -9看系统怎么恢复,把故障演练当成日常操作。Redis 6 的集群生态已经足够成熟,真正让你翻车的往往不是 Redis 本身,而是对流程不熟、对参数理解不到位。把这套东西玩熟了,你会发现高可用架构并不是什么玄学。

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

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

立即咨询