自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

Redis在后端中的应用

  1. 01为什么后端系统会引入 Redis
  2. 02Redis 为什么快,以及快的代价
  3. 03别背命令:从业务问题选择 Redis 数据结构
  4. 04Key 设计决定 Redis 能不能长期维护
  5. 05缓存不是加速开关:从一次击穿事故理解 Cache Aside
  6. 06数据库和缓存注定会短暂不一致
  7. 07登录态放进 Redis 后,问题才真正开始
  8. 08计数与限流:一个 INCR 解决不了所有并发
  9. 09排行榜不是把分数塞进 ZSet 就结束了
  10. 10分布式锁与幂等:SET NX 只是起点
  11. 11消息与异步解耦:Redis Stream 不是迷你 Kafka
  12. 12事务、Pipeline 与原子性不是一回事
  13. 13Lua 把读—判断—写放进一次不可穿插的执行
  14. 14RDB 与 AOF:你愿意承受多大的数据丢失窗口
  15. 15高可用不是零故障:主从、Sentinel 与 Cluster
  16. 16半夜告警之后:Redis 生产排障清单
正在加载课程章节内容
课程编程Redis在后端中的应用高可用不是零故障:主从、Sentinel 与 Cluster

高可用不是零故障:主从、Sentinel 与 Cluster

上一章把 RDB、AOF 和恢复演练讲清楚后,一个更容易混淆的问题就冒出来了:既然磁盘里有数据,主机宕机时为什么不能立刻换一台机器继续服务?

因为“数据能恢复”和“服务能接管”是两件事。持久化关心进程重启后能找回多少数据;高可用还要回答谁发现故障、谁有资格接管、客户端怎样找到新地址,以及旧主重新出现后怎么处理。更麻烦的是,这些动作都发生在网络可能分区、节点状态并不一致的时刻。

所以这章不把主从、Sentinel、Cluster 当成三套需要背诵的名词。我们沿着一个真实接口往下看:订单服务刚把 order:o20260819:status 从 pending 改成 paid,Redis 返回成功,主节点随即失联。新主上到底有没有这次写入?客户端会不会还连着旧主?如果为了扩容用了 Cluster,这个 key 又该发往哪个分片?

先给结论:高可用只能缩短故障时间、限制故障范围,不能把故障变没。 主从复制提供数据副本,Sentinel 给非分片主从组做自动接管,Cluster 把数据与故障域拆到多个分片。三者都建立在异步复制之上,因此可用性、延迟和数据安全之间始终要做选择。

落到项目上,最好先写两项预算。恢复时间目标回答“从主节点出故障到接口重新可用,最多能等多久”;恢复点目标回答“切换完成后,最多允许回退多少数据”。缓存详情页也许能接受十几秒降级和重新预热,登录会话丢失会把用户踢下线,支付状态回退则可能根本不能接受。没有这两项预算,“高可用”就只剩节点数量,团队也无法判断一次切换到底算成功还是失败。

还要先划清故障单位:Redis 进程、虚拟机、宿主机、机架、可用区和客户端网络不是同一层。主从节点放在同一台宿主机,只能抵御进程故障;三个 Sentinel 共用一个网络出口,也无法抵御出口中断。副本数量只有与独立故障域结合,才真正增加生存概率。

支付事实、订单事实这类不能丢的数据,仍应由具备明确事务与恢复承诺的数据库保存。Redis 可以保存派生状态、加速读取或承接短期协调,但“部署了三个节点”并不会自动把它变成强一致账本。


异步复制先留下了一道时间缝

Redis 主从复制的正常路径很直接:客户端把写命令发给主节点,主节点在本地执行,再把改变数据集的命令流发送给一个或多个从节点。从节点重放同一条命令流,逐步追上主节点。主节点不会等每个从节点都执行完才回复普通写请求,这正是它延迟低、吞吐高的一部分原因,也是已确认写入可能在故障转移中丢失的根源。

设想订单接口连续写入 W1 和 W2。主节点都返回成功,从节点只处理完 W1。如果主节点此刻永久损坏,Sentinel 或 Cluster 能提升的候选节点里并没有 W2。自动切换可以让服务重新可写,却无法从空气里还原那条尚未到达副本的命令。

Redis 主从复制确认边界:主节点已返回成功,从节点只复制到 W1,W2 仍处于复制延迟窗口时主节点故障

这件事在应用层会表现得很诡异。POST /orders/o20260819/pay 已经向调用方返回成功;切换完成后,GET /orders/o20260819 却读到旧状态。如果调用方因超时再次提交,系统还可能同时面对“第一次是否生效不确定”和“第二次是否会重复执行”两个问题。因此,故障转移之前就要有业务幂等键、数据库对账或补偿流程,不能把所有正确性都押在 Redis 副本上。

复制状态可以从 INFO replication 和 ROLE 观察。主节点会维护复制标识与复制偏移量;从节点也会报告自己已经处理到哪里。偏移量差值可以帮助判断复制缺口,但它不是业务数据条数,更不能直接换算成“丢了几个订单”。同一条命令的编码长度不同,一次 Lua 或事务也可能推进不同字节数。

redis
INFO replication
ROLE

监控时至少要把以下信号放在一起看:从节点是否在线、复制链路是否持续中断、主从偏移量差是否扩大、最近一次 I/O 距现在多久、是否频繁发生全量同步。只看 connected_slaves:2 很容易得到虚假的安全感:两个从节点都连着,不代表它们足够新,也不代表应用真的会在主故障后切到正确地址。


断线后能补课,取决于积压缓冲区还记不记得

从节点短暂断线后重新连接,不一定要把全部数据再传一遍。主节点维护一段环形的复制积压缓冲区,保存最近的复制命令流;从节点带着复制标识和自己的偏移量请求继续同步。如果这段历史仍在缓冲区中,主节点只补发缺失部分,这叫部分重同步。

复制标识用来区分“哪一段数据历史”,偏移量表示这段历史已经推进到哪里。只比较偏移量而忽略复制标识是不够的:网络分区后旧主和新主可能各自继续推进,数值相同也不代表数据相同。节点被提升后会开启新的复制历史,同时保留旧历史的关联信息,其他从节点在满足条件时仍可部分重同步;这既减少切换后的全量复制,也避免把两条分叉历史误认为一条。

问题是环形缓冲区会不断覆盖旧内容。假设写入流量平均为每秒 5 MB,网络断了 30 秒,仅缺口就接近 150 MB;如果 backlog 只有 64 MB,旧历史早已被覆盖。此时部分重同步无法完成,只能走全量重同步:主节点生成数据集快照,把快照传给从节点,从节点加载后再追赶期间积累的新写入。全量同步会带来 CPU、内存、网络与加载时间的共同压力,多个从节点同时重连时尤其明显。

Redis 断线重连中部分重同步与全量重同步的流程和代价对比

backlog 因此不是“越大越专业”的固定数字,也不该照抄一份配置。可以先按下面的思路估算:

text
所需缓冲区 ≈ 峰值复制字节率 × 希望覆盖的最长断线时间 × 安全系数

峰值复制字节率要从真实业务测,最长断线时间应覆盖常见网络抖动、节点滚动发布与连接重建。安全系数留给突发写入和估算误差。算完还要做断链演练,确认实际发生的是部分重同步,而不是每次网络闪断都触发全量同步。

全量同步不一定必须先把 RDB 落到主节点磁盘。Redis 可以配置无盘复制,让子进程直接把快照流发送给从节点;这能减少慢磁盘的中转开销,却没有消除 fork、生成快照、网络传输和从节点加载的成本。换句话说,改变的是传输路径,不是把全量同步变成免费操作。

下面的实验台把写入速率、断线时间和 backlog 放在一起。先固定 backlog,逐渐拉长断线时间,观察部分重同步在什么位置变成全量重同步;然后扩大 backlog,看看你为更长的断线窗口付出了多少常驻内存。


WAIT 与 WAITAOF 只能加强确认,不能改写系统性质

如果某一类写入比普通缓存更新更重要,可以在同一条客户端连接上先写,再调用 WAIT。它等待指定数量的从节点确认已经处理到这条连接此前写入对应的复制偏移量。达到数量就返回,超时也会返回;返回值是已经确认的副本数,所以业务代码必须比较结果,不能把“命令结束了”误解成“目标已满足”。

redis
SET order:o20260819:status paid
WAIT 1 200

WAIT 1 200 的意思是最多等 200 毫秒,希望至少一个从节点确认收到此前写入。它不是把 Redis 变成强一致系统,也不保证确认的从节点一定会成为新主。故障转移、持久化配置和网络分区仍可能让写入消失。更准确的说法是:WAIT 用一次额外等待换取更小的数据丢失概率。

Redis 7.2 起提供 WAITAOF,把确认推进到 AOF 刷盘层。WAITAOF numlocal numreplicas timeout 会等待当前连接此前的写入在本机和指定数量从节点上完成 AOF fsync,并返回两个整数:本机完成数、从节点完成数。numlocal 设为 1 时本机必须开启 AOF;命令只能在主节点执行。它仍然要检查返回值,也仍然不等于强一致。

Redis 写入安全阶梯:主节点执行、WAIT 从节点确认收到、WAITAOF 确认写入 AOF,延迟与安全性同步增加

在 Redis 8.2.2 的单节点配置上,端口为 6415,开启 AOF 并使用 appendfsync always,但不配置从节点,执行下面这组命令:

redis
SET order:20260819:status created
WAIT 1 100
WAITAOF 1 1 100

控制台显示:

text
OK
0
1) (integer) 1
2) (integer) 0

这里 WAIT 返回 0,因为没有任何从节点确认;WAITAOF 1 1 100 同时要求本机和一个从节点完成 AOF fsync,返回的两个值分别是本机与从节点完成数,本机为 1、从节点为 0。这段结果只验证命令在单节点条件下怎样表达“本机已刷盘,但没有副本确认”,没有冒充多节点故障切换。测试结束后,6415 端口的进程与临时目录均已清理。

不要对所有缓存写入统一追加 WAIT 或 WAITAOF。先给业务数据分级:可随时重建的缓存直接接受异步复制;允许小窗口丢失的状态可按风险使用 WAIT;若一条记录要求严格事务、审计和确定恢复,通常应回到权威数据库,而不是继续给 Redis 叠确认命令。

主节点还可以通过 min-replicas-to-write 和 min-replicas-max-lag 限制写入:只有至少 N 个从节点的延迟不超过 M 秒时才接受写。它能阻止一个长期失去副本保护的孤立主继续收写,从而限制脑裂期间的损失窗口;代价是在副本或网络异常时主动牺牲写可用性。它是风险闸门,不是共识协议。


读从节点会把复制延迟送到业务接口

从节点默认只读,把查询分给从节点确实能扩展读吞吐,但它把“允许读到多旧”变成了接口契约。用户刚修改头像,写请求发到主节点;详情页下一次读取被负载均衡到落后的从节点,于是页面又显示旧头像。过几百毫秒再刷新恢复正常,这类问题最难排查,因为数据库和最终状态都没有错。

过期 key 还有一层容易被误解的细节。Redis 的过期删除主要由主节点驱动,主节点过期或淘汰 key 后把相应删除传播给从节点。副本在删除命令到达前,内存里可能仍保留那个 key;对普通读取,它会依据逻辑时间避免返回已经过期的值。但这只能处理 TTL 已到的情况,不能解决“值已经更新、从节点还没追上”的普通复制延迟。

所以读写分离不能只配一个 role: "slave" 就结束。常见策略有三种:更新后的短时间内粘到主节点;只有容忍陈旧的接口读从节点;用版本号或业务时间戳检测明显回退并回主重读。权限撤销、库存确认、支付结果这类接口通常不该为了分担读压力随意读从。

ioredis 的 Sentinel 模式可以请求从节点连接,Cluster 模式也能设置 scaleReads: "slave"。但官方客户端文档明确提醒,刚 SET 后从副本 GET,结果可能因为复制延迟而不同。默认把 Cluster 读取发往主节点,往往是更稳妥的起点;只有接口的新鲜度预算写清楚后,再逐类放开读从。


Sentinel 解决的是谁来接管,不是数据凭空完整

只有主从复制时,主节点宕机后仍要人工挑一个从节点、执行提升、让其他从节点改为跟随它,并通知所有客户端。Sentinel 把这套控制面自动化了。它持续监控主从组,发布事件,在需要时组织故障转移,同时为客户端回答“这个服务当前的主节点在哪里”。Sentinel 不代理业务命令,也不保存业务数据。

一次故障转移至少经过两道判断。单个 Sentinel 在 down-after-milliseconds 时间内收不到主节点的有效响应,会把主节点标记为主观下线,也就是 SDOWN。它再询问其他 Sentinel;同意主节点不可达的数量达到该主从组配置的 quorum 后,才形成客观下线 ODOWN。

down-after-milliseconds 不是越小越敏捷。设得过小,一次短暂的网络排队或主线程阻塞就可能触发误判和无谓切换;设得过大,真正宕机时又会拉长恢复时间。它应该高于正常网络和延迟抖动,并结合应用超时、Sentinel 探测记录与故障演练调整。failover-timeout 也不是简单的“整个切换必须在这段时间内结束”,它还参与限制重复故障转移、重新尝试和配置传播的节奏。

quorum 还不是“马上切换”的全部条件。真正执行故障转移的 Sentinel 需要取得已知 Sentinel 的多数授权。假设有 5 个 Sentinel、quorum 配成 2:两个观察者同意即可触发 ODOWN,但执行者仍要拿到至少 3 票授权。把 quorum 和多数授权混成一个数,会让人错误估计网络分区时到底能不能切换。

Redis Sentinel 故障转移决策链,区分法定票数判定客观下线与多数授权选举执行哨兵

得到授权后,Sentinel 还要挑从节点。它会先排除与主节点断开太久、不适合接管的副本,再依次考虑 replica-priority、已处理的复制偏移量和运行标识:较小的优先级值更优,偏移量更大的副本数据通常更新;优先级为 0 的副本不会被提升。机房位置、机器规格或业务用途并不会被 Sentinel 自动理解,必须通过部署和优先级配置表达出来。

下面的决策器可以分别调整 Sentinel 总数、quorum、网络可见性和候选副本状态。重点观察两个反直觉场景:达到 quorum 但拿不到多数授权时不会切换;数据最新的副本也可能因为优先级或长时间断线而落选。

一个最小配置可以这样写:

conf
sentinel monitor order-cache 10.20.0.11 6379 2
sentinel down-after-milliseconds order-cache 5000
sentinel failover-timeout order-cache 60000
sentinel parallel-syncs order-cache 1

生产上常用三个 Sentinel,但“奇数个”不是护身符。三个进程如果都在同一台宿主机、同一个机架或同一网络出口,故障域仍然只有一个。更合理的部署是把 Sentinel 分散到能够独立失效的位置,并确保它们看到的网络路径接近应用实际路径。上线前用 SENTINEL CKQUORUM order-cache 检查当前配置是否同时满足 quorum 与多数授权条件。


客户端必须认识 Sentinel,不能把旧主地址写死

Sentinel 完成切换后不会神奇地改掉应用配置文件。客户端要先连接一组 Sentinel,以主从组名称查询当前主节点;连接断开后重新询问,而不是继续重连写死的旧地址。把 Redis 主地址塞进环境变量、让 Sentinel 在后台运行,却仍使用普通单节点客户端,自动故障转移只完成了一半。

ioredis 的连接方式如下:

js
import Redis from "ioredis";
 
export const redis = new Redis({
  sentinels: [
    { host: "10.20.0.21", port: 26379 },
    { host: "10.20.0.22", port: 26379 },
    { host: "10.20.0.23", port: 26379 },
  ],
  name: "order-cache",
  username: process.env.REDIS_USERNAME,
  password: process.env.REDIS_PASSWORD,
  sentinelUsername: process.env.SENTINEL_USERNAME,
  sentinelPassword: process.env.SENTINEL_PASSWORD,
  enableReadyCheck: true,
  maxRetriesPerRequest: 1,
  connectTimeout: 1000,
});
 
redis.on("error", (error) => {
  console.error("redis connection error", error.message);
});

这里只给出拓扑相关的骨架,超时与重试次数仍应按接口预算调整。ioredis 会在故障转移期间重新向 Sentinel 查询主节点,并可能暂存命令等待连接恢复。对 HTTP 请求来说,这会产生一个业务问题:上游已经超时并重试,原连接里排队的写命令后来又成功了。因此写操作仍要使用请求幂等键;缓存读取则可以设置较短超时,快速进入限流回源或降级,而不是让请求一直挂在离线队列里。

Sentinel 发现的新主也可能使用客户端无法访问的内网地址,容器、NAT 和多网络平面下尤其常见。演练不能只看 Sentinel 日志出现 +switch-master,还要从应用运行位置验证:能发现新地址、能完成认证、能建立 TLS、能在请求截止时间内恢复,并且旧连接不会继续接受业务写入。


脑裂不是两个节点都坏了,而是两边都以为自己能工作

网络分区比进程崩溃更棘手。旧主还活着,只是与 Sentinel 和从节点所在的多数分区失联;部分应用实例仍能访问旧主并继续写。多数分区里的 Sentinel 判断旧主下线,提升一个从节点为新主,另一批应用开始写新主。此时系统里同时出现两条写入历史。

网络恢复后,旧主通常会被降级并重新同步新主。它在分区期间独有的写入不会自动合并,而会被新主的数据覆盖。Sentinel 没有“制造”脑裂,它只是无法在不可靠网络中阻止旧主对仍连接它的客户端服务。

网络分区造成旧主与新主同时写入、恢复后旧主独有写入丢失的过程

min-replicas-to-write 与 min-replicas-max-lag 可以让失去足够健康副本的旧主停止接受写入,从而压缩风险窗口。客户端侧还要限制重试、做幂等、把不可丢事实写回权威数据库。更强的基础设施隔离、可靠的 fencing 机制也能避免两个执行者同时修改外部资源,但单靠 Redis 分布式锁或 Sentinel 票数并不能自动完成所有隔离。

故障演练要同时检查两种结果:恢复用了多久,以及恢复后丢了什么、重复了什么。只记录“切换 4 秒完成”而不核对写入序列,会把高可用报告写成一份漂亮但危险的计时成绩。


Cluster 先解决分片,再在每个分片上做高可用

当单个主节点的内存或写吞吐已经成为明确瓶颈,才轮到 Redis Cluster。它把 key 空间划成 16384 个槽,每个主分片负责一部分槽。默认槽位计算是对 key 做 CRC16/XMODEM,再对 16384 取模。客户端拿到“槽位到节点”的拓扑表后,可以直接把命令发到负责节点,而不必经过一个中心代理。

这意味着 Cluster 客户端不是普通连接池。它要发现节点、维护槽位表、按 key 路由,还要理解重定向。请求误发到错误节点,稳定状态下会收到 MOVED,表示这个槽已经由另一个节点长期负责,客户端应重试并刷新本地映射。迁槽期间会遇到 ASK,它只表示这一次请求临时去目标节点;客户端要先发送 ASKING,再发原命令,不能立刻把整个槽永久改到目标节点。

Redis Cluster 16384 槽的客户端路由、ASK 临时重定向与 MOVED 永久重定向

迁槽时,源节点把槽标为迁出,目标节点标为导入,然后逐个迁移该槽内的 key。尚未迁走的 key 仍由源节点处理;已经迁走、在源节点查不到的 key 会触发 ASK。等整个槽完成移交,集群再宣布新的永久归属,之后错误路由才返回 MOVED。所以扩容不是把一行配置改成四个节点,它是一段会增加网络、CPU、重定向和尾延迟的在线数据搬迁过程。

Cluster 的故障转移仍以每个主分片的异步副本为基础。某个主分片失联后,它的从节点是否能提升,取决于集群多数侧对拓扑和故障的判断;如果负责一部分槽的主节点及其副本同时不可用,相关槽就无法服务。启用要求完整槽覆盖的配置时,甚至可能让整个集群拒绝请求。分片缩小了单节点数据量,却把“哪个槽还能服务”加入了故障分析,不能只看集群总节点数。

扩容前还要观察槽内 key 的真实分布。槽数量均匀不代表负载均匀:一个包含巨大 Hash 的 key 仍只属于一个槽,一个爆款商品的热 key 也只会打到一个主分片。槽均衡解决的是分配框架,big key、hot key 和业务访问倾斜仍要单独治理,这正好衔接下一章的性能故障主题。

下面的路由实验台可以直接输入 key,按 Cluster 的 CRC16 规则计算槽位;再把一个槽切换到迁移状态,观察 ASK → ASKING 与 MOVED → 刷新槽位表 的区别。也可以一次输入多个 key,看哪些命令会遇到 CROSSSLOT。


Hash Tag 是数据建模约束,不是修错小技巧

Cluster 中大多数多 key 命令、事务与 Lua 脚本要求涉及的 key 位于同一槽。购物车结算可能同时访问购物车、优惠券占用和结算幂等记录:

text
cart:{u42}
coupon:{u42}
checkout:{u42}:request:r9001

花括号中第一个非空片段会参与槽位计算,因此这些 key 都按 u42 计算,可以在同一槽里执行需要原子边界的逻辑。如果写成 cart:u42、coupon:u42,它们大概率落在不同槽,多 key 命令会返回 CROSSSLOT。

但也不能把所有 key 都写成 {orders}:...。这样虽然再也不跨槽,却把全站订单压进一个槽、一个主分片,Cluster 的分片能力被自己取消了。Hash Tag 应围绕真正需要共同操作、且基数足够分散的业务聚合根设计,例如用户 ID、订单 ID 或租户 ID。上 Cluster 之前先盘点 Lua、事务、MGET/MSET、集合运算、Pipeline 与批量删除,往往比先买节点更重要。

ioredis 的 Cluster 客户端可以从少量启动节点发现完整拓扑:

js
import Redis from "ioredis";
 
export const redisCluster = new Redis.Cluster(
  [
    { host: "10.30.0.11", port: 6379 },
    { host: "10.30.0.12", port: 6379 },
    { host: "10.30.0.13", port: 6379 },
  ],
  {
    enableReadyCheck: true,
    scaleReads: "master",
    redisOptions: {
      username: process.env.REDIS_USERNAME,
      password: process.env.REDIS_PASSWORD,
      connectTimeout: 1000,
      maxRetriesPerRequest: 1,
    },
  },
);

scaleReads: "master" 明确让读取走主分片,避免一开始就引入从节点陈旧读。需要读扩展时可以改为 slave 或自定义选择函数,但业务必须接受复制延迟。Pipeline 也要按客户端规则设计:ioredis 会按节点组织自动 Pipeline;显式 Pipeline、事务和脚本涉及的 key 仍受路由与同槽约束。迁槽期间若整个 Pipeline 重试,只有满足客户端的安全条件才会自动重发,写操作不能假设“看到重定向就无脑重试”。

Cluster 内部已经包含分片主从与故障转移机制,不需要再拿 Sentinel 监控每个 Cluster 主节点。真正需要补的是集群感知客户端、节点与可用区布局、槽位均衡、迁槽限速、容量余量和故障演练。


选型时先问故障预算,不要默认上 Cluster

很多架构图把“单机 → 主从 → Sentinel → Cluster”画成成熟度升级路线,仿佛最后一步一定是 Cluster。实际选择应该从瓶颈和故障预算倒推:

方案主要解决什么仍然存在的边界适合的起点
单节点 + 持久化重启恢复、最低复杂度节点故障期间不可用,恢复依赖人工或平台可重建缓存、开发环境、能接受恢复时间的小系统
主从复制数据副本、可选读扩展默认不自动接管,异步复制可能丢已确认写需要副本或离线读取,但已有外部编排能力
主从 + Sentinel非分片数据的自动发现与故障转移单主写入与容量上限仍在,也仍可能脑裂和丢复制窗口内写入数据量能放进单机、写吞吐够用,但需要自动接管
Redis Cluster分片容量、写水平扩展、分片级故障转移客户端、同槽建模、迁槽和运维复杂度显著增加已证实单主内存或写吞吐是瓶颈,并能承担分片改造

如果 40 GB 数据能稳定放进一台 128 GB 机器,写吞吐也远未到瓶颈,只是要求主故障后自动恢复,主从加 Sentinel 往往比 Cluster 更容易验证。反过来,数据集持续逼近单机安全水位,或者单主 CPU 已经被写命令打满,再继续加从节点也不能扩展写能力,Cluster 才有明确收益。

先给数据分类。明确哪些 key 可重建、哪些允许陈旧、哪些允许丢失一个复制窗口,以及哪些业务事实根本不该只放在 Redis。

再写故障预算。包括可接受的恢复时间、单次最多丢多少写入、故障期间是否允许拒绝写,以及客户端超时后怎样幂等重试。

用真实指标证明瓶颈。观察主节点内存安全水位、写 CPU、网络、热 key 和延迟;没有单主容量或写吞吐证据时,不把 Cluster 当默认答案。

最后做破坏性演练。在隔离环境依次模拟主进程退出、主从断链、Sentinel 少数分区、应用侧超时、Cluster 迁槽与节点失效,并逐条核对业务写入,而不只看“集群恢复正常”。


上线检查不是看节点都亮绿灯

一次可交付的高可用验收,至少要回答这些具体问题:

  • 主故障后,应用在多少时间内恢复读写?这个时间是否小于接口和上游重试预算?
  • 故障前最后一批写入中,哪些已到副本、哪些只在旧主、哪些被调用方重试?
  • Sentinel 的 SDOWN、ODOWN、quorum 与多数授权是否按预期发生?候选副本为什么被选中?
  • 网络分区时,旧主是否会因健康副本不足而停止接受写入?仍连旧主的客户端会怎样失败?
  • Cluster 客户端能否处理 MOVED 与 ASK,公布的节点地址从应用网络是否可达?
  • 多 key 命令、事务、Lua 和批处理是否已做同槽设计?Hash Tag 会不会制造新的热点槽?
  • Redis 完全不可用时,缓存接口是限速回源还是降级?状态类接口是失败关闭还是转向权威存储?

好的高可用方案不会承诺“永不出错”,而会把每种错误限制在事先写清楚的范围里:最多停多久、最多旧多久、最多丢什么、谁负责补偿。能用演练和对账证明这些边界,才算真正拥有高可用。

1
5 个 Sentinel 中有 2 个认为主节点不可达,quorum 配置为 2,但只有这 2 个 Sentinel 能互相通信。此时最准确的判断是什么?
2
准备把订单缓存迁到 Redis Cluster,哪些工作应在迁移前完成?
上一章RDB 与 AOF:你愿意承受多大的数据丢失窗口下一章半夜告警之后:Redis 生产排障清单