Redis 高可用
Redis 高可用是在部分节点或连接故障后,让其他节点接管服务,并让客户端找到可用节点的能力。复制准备数据副本,Sentinel 协调非分片主从切换,Cluster 则同时管理分片和各分片的故障转移。面试里需要讲清的不只是部署了几个节点,还包括谁决定切换、客户端去哪里读写,以及切换后哪些写入可能不见。
本文以 Redis 6、7 为基线。RDB、AOF 与落盘策略见Redis 持久化,这里重点解释运行中的复制、路由和接管。
1. 面试核心
先记住这几条,再读正文- 副本保存同一份数据,分片保存不同部分的数据。增加副本不等于扩展写入容量,增加分片也不等于有了故障备份。卡 · 主从、Sentinel 与 Cluster
- 主从复制默认异步。
PSYNC根据复制历史与偏移量判断能否补发;缺失命令已超出 backlog 时,需要全量同步。卡 - 复制延迟要结合主从偏移量、链路和副本处理能力排查。
WAIT确认副本进度,既不等于落盘,也不保证所有故障后都能保住写入。卡 · 全量同步与部分重同步 - Sentinel 先判断下线,再选出执行切换的 Sentinel,并挑选合格副本接管。故障判定的 quorum 与切换所需多数授权是不同条件。卡 · 下线判定与切换授权
- Cluster 把键映射到 16384 个槽,每个槽由一个主节点负责;同槽多键操作、节点互通和每个主节点的副本都影响部署是否可用。卡 · 哈希槽与同槽操作
MOVED表示槽路由需要更新;ASK只让本次请求临时去导入节点,并在同一连接先发送ASKING。卡- 数据迁移需要同时处理已有数据、迁移期间的写入和切换后的核对。只复制一份快照,不能代表在线迁移已经完成。卡 · 槽迁移
- 自动切换缩短恢复过程,但异步复制仍有已确认写入丢失的窗口。持久化、可用副本和业务重试分别解决不同问题。卡 · 复制确认与写入限制
2. 主从、Sentinel 与 Cluster
副本是另一台 Redis 实例上对同一数据集的复制;分片则把一个逻辑数据集分成多部分,由不同主节点负责。假设有 100 万个键,一主两副本大体上各保存这 100 万个键;分到三个主节点后,每个主节点只负责其中一部分。分布是否均衡,还要看键的大小和访问量。
| 方式 | 数据怎样分布 | 主节点故障后 | 适用判断 |
|---|---|---|---|
| 主从复制 | 主节点和副本保存同一数据集 | 复制本身不自动完成主节点选举与客户端切换 | 作为其他高可用机制的基础,也可为可接受延迟的读取提供副本。 |
| Sentinel + 主从 | 数据仍由一个主节点承接写入 | Sentinel 协调提升副本,客户端发现新主 | 数据和写入压力仍能由单主承接,重点是自动接管。 |
| Redis Cluster | 多个主节点各自负责一部分槽,每个主节点可有副本 | 相应副本参与该分片的故障转移 | 需要分散数据容量和请求压力,业务能接受跨槽操作限制。 |
Sentinel 不是数据代理,请求通常直接发给 Redis 主节点。Cluster 客户端也会按槽路由直接访问对应节点。两者都需要客户端支持拓扑变化;只把一个旧主地址写进配置,无法自动享受完整的故障切换能力。
节点应分布在独立故障域中。把所有主从和 Sentinel 放在同一台机器,能验证进程级切换,却不能应对这台机器整体故障。Cluster 的副本也应避免与自己的主节点一起故障。
3. 主从复制
主从复制把主节点的数据和后续修改传给副本。副本默认只读;主节点收到写请求后执行命令,再将修改传播出去,通常不会等待副本完成才回复客户端。
例如两个独立实例已经监听本机的 7100 和 7101 端口,在 7101 的连接上执行:
REPLICAOF 127.0.0.1 7100
INFO replication
REPLICAOF 返回 OK 只表示接受配置。副本还需要建立连接和同步数据;之后 INFO replication 中的 master_link_status:up 才说明复制连接已建立。角色字段沿用历史名称 role:slave。它们都不能单独证明副本已经包含某一次业务写入,还需查看复制进度。
3.1 全量同步与部分重同步
全量同步先传输一个完整数据集,再接上生成这份数据期间积累的修改。初次建立复制,或者无法补齐断连期间的缺口时,会走这条路径。Redis 可通过 RDB 文件或无盘传输发送数据集;这里使用 RDB 格式,不意味着必须事先配置定时 RDB 备份。
同步期间主节点仍可接收写入,但生成和传输快照、保留增量、加载数据都要消耗资源。副本加载大型数据集会影响服务;“异步复制”不等于同步过程没有停顿或成本。
部分重同步只补发副本缺失的那段复制命令流。判断依据有两个:
- 复制 ID 标识一段复制历史。只看偏移量数值相同,不能证明两边经历了同一批修改。
- 复制偏移量表示在这段命令流中的位置。主节点的 backlog 保留近期命令;副本缺失的部分仍在其中,才有条件补发。
PSYNC 带着复制 ID 和偏移量请求恢复。历史不匹配,或者缺口已经超出保留范围,就需要全量同步。主从切换时 Redis 还会保留第二复制 ID 及其有效偏移边界,帮助匹配旧历史;这不能使任意分叉历史重新合并。
repl-backlog-size 应结合复制流量和允许断连时长设置。例如每秒产生约 2 MiB 复制流量,希望覆盖 30 秒断连,粗略需要超过 60 MiB,再为波动预留空间。这里估算的是复制流字节,不是业务记录数,也不是一个固定容量保证。
3.2 复制延迟
复制延迟是副本尚未跟上主节点进度的状态。客户端刚写主节点就读副本,可能读到旧值。副本连接为 up 也可能存在积压。
排查先确认两端属于可比较的复制历史,再观察偏移量差距是否扩大,并结合复制链路、主节点发送能力、副本 CPU、网络和加载状态判断。字节偏移差不能直接换算成落后多少秒;主节点看到的副本确认也有更新时间。
减少延迟需要处理实际瓶颈,例如限制大 key 操作、提供足够带宽和副本处理能力,或避免频繁全量同步。调大 backlog 主要帮助断线后补发,不能让持续处理不过来的副本突然追上。
读取必须观察刚完成的写入时,可以按业务约束读主节点;不过主节点切换后,仍要面对新主可能缺少这次写入的问题。需要更严格的状态保证时,应重新评估 Redis 在这条业务链路中承担的职责。
3.3 复制确认与写入限制
WAIT 等待当前连接此前的写入被指定数量副本确认,超时返回实际确认数,已经执行的写入不会回滚。连接池场景必须保住这条连接的上下文,不能在另一条随机连接上调用后便认定它覆盖了原写入。
副本确认复制进度与 AOF 同步到持久存储是两回事。WAITAOF 从 Redis 7.2 起提供落盘确认,具体返回值和配置条件见复制确认与落盘确认。两条命令都不能替代故障后接管节点的选择规则。
min-replicas-to-write 与 min-replicas-max-lag 可让主节点在“足够数量、最近有确认的副本”这一条件不满足时拒绝写入。这里检查的是副本状态及最近确认的时间,没有逐条等待本次写入复制成功。限制越严格,副本或链路异常时越容易停止写服务,需要接受可用性上的代价。
4. Sentinel
Sentinel 是监控非 Cluster 主从实例并协调故障转移的进程。它还向客户端提供当前主节点地址。客户端获得地址后连接 Redis,普通读写不经过 Sentinel。
4.1 下线判定与切换授权
主观下线是一个 Sentinel 在配置时间内未收到有效响应,认为实例不可用。客观下线则是足够多 Sentinel 同意主节点不可用,达到该主节点配置的 quorum。
判定主节点下线后,还要选出一个负责执行本轮切换的 Sentinel。候选 Sentinel 需要得到多数 Sentinel 的授权;若配置的 quorum 更高,也要达到它。获得授权的 Sentinel 再选择要提升的 Redis 副本。这里有两种对象:选执行者是 Sentinel 之间的协调,选新主是从 Redis 副本中挑选。
例如 5 个 Sentinel、quorum=2,只有两个还能互通时,它们能够共同判定旧主客观下线,却拿不到至少 3 个 Sentinel 的多数授权。把 quorum 调成 1 也不能绕过多数授权。相应自测见Sentinel 的 quorum。
4.2 提升副本与客户端切换
Sentinel 会先排除不可达、长时间失联或明确不允许提升的副本,再对合格候选者排序。Redis 6、7 中,replica-priority 数值越小越优先,0 表示不参与提升;优先级相同时比较复制偏移量,再用运行 ID 打破平局。因此不能简化成“永远提升数据最新的副本”。
切换的主要过程是:
- 取得本轮故障转移授权,选出合格副本。
- 让该副本停止跟随旧主,提升为新主。
- 把其他副本重新指向新主,并传播新的主节点配置。
- 客户端重新发现主地址、建立连接,继续业务请求。
旧主恢复后,会被重新配置为新主的副本。旧主独有而新主没有的修改不会自动合并回来;如果业务已经把这次写入视为完成,就要面对数据回退。
客户端切换还需要处理结果未知的请求:连接断开可能发生在命令执行前,也可能发生在执行后、响应到达前。盲目重试一次 INCR 可能多加一次;需要业务幂等依据、有限的重试次数和总超时。
5. Redis Cluster
Redis Cluster 将数据分到多个主节点,同时为每个主节点维护副本和故障转移机制。它自行协调这些工作,不依靠 Sentinel 来选举 Cluster 主节点。
5.1 哈希槽与同槽操作
哈希槽是 Cluster 分配数据的单位,槽编号为 0 到 16383。普通键按 CRC16(key) mod 16384 得到槽,再由槽映射到负责它的主节点。扩缩容改变的是槽的归属,而不是把节点放进一致性哈希的哈希环重新映射。
hash tag 是键名中参与槽计算的非空花括号内容,可以让相关键落在同一槽。例如在一个已建立的 Cluster 节点连接上执行:
CLUSTER KEYSLOT {user:42}:profile
CLUSTER KEYSLOT {user:42}:orders
两条命令都返回 15880,因为参与计算的都是 user:42。解析时使用第一个 { 和其后第一个 } 之间的内容;如果这一对为空,则对完整键计算,不再寻找下一对。因此 foo{}{bar} 不会按 bar 计算。
在槽 15880 的主节点上,可以执行:
MSET {user:42}:profile Ada {user:42}:orders 2
MGET {user:42}:profile {user:42}:orders
第一条返回 OK,第二条依次返回 Ada、2。把第二个键换成不同槽的键,会收到 CROSSSLOT。事务和访问多个键的 Lua 脚本也要遵守同槽约束。客户端可以把某些跨槽请求拆开,但不能因此保留原本单条命令的原子性。
同槽只保证这些键被放在一起,不保证访问负载均匀。把所有订单都写成同一个 tag,会把它们集中到一个主节点;一个大热 key 也不会被自动切成多份。热点处理见热点 key。
5.2 部署与故障转移
三主三副本是一个便于理解的部署例子:三个主节点分担槽,每个主节点各有一个副本。只有三个主节点而没有副本,仍然能分片;其中一个主节点故障后,却没有对应副本来接管。
节点不仅要开放客户端端口,还要让集群总线和复制所需连接互通。Redis 6、7 的集群总线端口默认是数据端口加 10000;容器映射和地址通告要让对端及客户端真正能够访问。看到六个进程都启动,不等于它们已经建立正确拓扑。
Cluster 节点通过集群通信传播状态。发现主节点故障后,合格副本会申请接管,并需要持有槽的主节点多数授权。副本的数据新旧、故障状态和选举轮次都会影响能否切换;相关参数与 Sentinel 的 quorum、replica-priority 不是同一套选举规则。
切换后,新主承担原来的槽,客户端按新的路由重发请求。这个过程依然使用异步复制,并没有让每次业务写入都先经过多数节点一致提交。
5.3 MOVED 与 ASK
MOVED 告诉客户端当前槽的负责节点。客户端应把这次请求发到目标节点,并更新槽映射;实现也可以刷新完整拓扑,以便处理同一轮变化的其他槽。映射将来仍可能变化,“更新路由”不代表该地址永久不变。
ASK 出现在槽迁移过程中:槽还属于源节点,但被请求的键可能已经移到目标节点。客户端只把本次请求交给目标,并在目标节点的同一连接先发送 ASKING。长期槽映射暂时保持原样。
假设槽 15880 正从 A 迁到 B,{user:42}:profile 已迁到 B。A 对它返回 ASK 15880 B的地址 后,在 B 的连接上执行:
ASKING
GET {user:42}:profile
这次 GET 可以返回 Ada。ASKING 只为下一条命令提供此次访问许可;在 B 上直接再发一次 GET,仍可能收到指回 A 的 MOVED。如果在 ASKING 与 GET 之间切换了连接,也不能依靠刚才的许可。
6. 数据迁移
Redis 数据迁移可能指把一个实例的数据搬到另一套环境,也可能指 Cluster 内部重新分配槽。两者都要处理并发写入,但不能直接当成同一条命令操作。
6.1 槽迁移
槽迁移会经历“路由仍指向源节点,但部分键已到目标节点”的中间状态。Redis 6、7 的基本过程是:
- 目标槽设为
IMPORTING,源槽设为MIGRATING,两端记录对方节点 ID。 - 逐批把该槽的键迁往目标。默认不使用
COPY选项时,MIGRATE成功会在目标恢复键并从源删除;网络错误时不能简单假定两边结果已完全确定,应核对后重试。 - 源节点上的键仍由源处理;源已不存在的键返回
ASK,让请求去目标。此时不能提前把所有请求固定发向目标。 - 确认源槽的键已经迁完,将槽所有者更新为目标并传播配置。旧路由请求随后通过
MOVED学到新归属。
如果同槽两个键一个还在 A、一个已在 B,MGET 无法在一个节点完成,会收到 TRYAGAIN。同槽是正常多键操作的前提,却不能保证迁移中每一次请求都立即成功。客户端应有限重试,而不是把暂时错误改写成“键不存在”。
迁移还需核对键类型、值和 TTL。只比较键总数会漏掉值不一致,也不能发现迁移后过期时间被重置。大 key 可能让一次序列化、传输或恢复占用较长时间,应控制批次并观察延迟;槽数量均匀也不代表字节量和请求量均匀。
6.2 跨实例迁移
跨实例迁移首先要明确允许停写多久、允许丢多少数据,以及迁移工具是否支持两端版本、数据类型和拓扑。
可以停写时,在停止写入后复制并恢复完整数据,核对结果,再切换客户端。迁移期间仍要写入时,则需要先同步已有数据,再持续追上增量,并在最终切换点隔离旧写入口。单独复制迁移开始时的 RDB,只覆盖那个时间点。
切换前应检查复制或迁移进度、失败重试、TTL 和代表性业务值。切换后继续观察新入口及旧入口残留请求,保留可执行的回退方案。如果新环境已经接受新写入,简单把地址改回旧环境会丢掉这些变化;回退也必须处理数据差距。
7. 故障与数据丢失
主节点回复成功,只能说明这次请求在当时达到了它的确认条件。下面这条顺序足以产生数据丢失:
- 主节点 A 执行写入并回复
OK。 - 副本 B 还没收到这次修改,A 就故障。
- B 被提升为新主,客户端切过去后看不到这次写入。
A 开启 AOF 也不保证 B 已有同一条数据;A 的持久化文件和 B 的复制进度是两个位置。需要分析的不是“有没有 AOF”或“有没有副本”这两个开关,而是故障时实际存活、被选为新主的节点掌握了哪些记录。
网络分区还可能让旧主短暂继续接受写入。Sentinel 的切换决定不会瞬间阻止所有客户端访问旧主;Cluster 也需要故障检测时间,少数派一侧随后失去继续服务的条件。限制落后副本下的写入能缩小窗口,却不把 Redis 变成线性一致的共识日志。
默认 cluster-require-full-coverage=yes 时,有槽失去可用负责节点会影响整个集群服务。设为 no 可以让满足其他服务条件的节点继续处理仍可用的槽,但不会补出缺失数据,也不能绕过多数主节点可达等条件。因此不能用一句“Redis 是 AP”推断网络分区时每个节点都可读写。
隔离的 Redis 7.4.9 小规模实验可以观察这些区别:
| 受控条件 | 观察结果 | 能说明什么 |
|---|---|---|
副本已断开,主节点执行 SET 后同连接 WAIT 1 50 | 分别返回 OK 和 0;主节点中仍有该值。 | 等待超时不会撤销写入。 |
| 随后终止旧主,手动提升没有这次写入的副本 | 旧的已复制值仍在,新值不存在。 | 未复制的确认写入不能靠提升副本恢复;这组实验没有让 Sentinel 自动选此副本。 |
| 5 个已互相发现的 Sentinel 只剩 2 个,quorum 为 2 | 可判定客观下线,CKQUORUM 报缺少多数授权。 | 下线判定和切换授权分离。 |
| Cluster 槽内只迁走两个键中的一个 | 单键得到 ASK,同槽 MGET 得到 TRYAGAIN;迁完后旧节点返回 MOVED。 | 迁移中数据位置和长期槽路由暂时不同。 |
这些是同一隔离环境内的进程与协议行为,不能据此推算生产环境故障概率、恢复时长或跨机房可用性。实际选型还要按业务允许的恢复点和恢复时间演练故障,见恢复目标与配置。
8. 常见问题
8.1 增加副本就能分担写入压力吗?
不能直接分担。一个主从复制组仍由主节点处理写入,每个副本还要应用同一条修改流。副本可以承担读取,但要接受复制延迟;要分散写入,通常需把业务数据分片,并处理跨片操作的代价。
8.2 新增 Cluster 主节点后,容量就已经扩展了吗?
还需要给新节点分配槽并迁移对应数据。仅加入一个空节点,不会自动把原有热点或数据均分过去。迁移完成后仍需检查各节点的字节占用、请求量及副本关系,不能只看槽数。
8.3 用 Sentinel 或 Cluster 后,分布式锁还可能失效吗?
仍可能。加锁写入未复制便发生切换,新主可能允许另一个客户端再次取得锁。高可用机制不会自动满足锁的互斥要求,租约超时也有独立风险,见分布式锁。
9 · 面试题卡片
本页题目与参考回答
9.1 Redis 单点服务可以怎样改造,主从、Sentinel 和 Cluster 如何选择?
考察重点:复制、自动接管、数据分片与客户端支持。
相关内容:主从、Sentinel 与 Cluster、故障与数据丢失。
参考回答
先确定瓶颈是接管能力还是单主容量,再选择主从加 Sentinel 或 Cluster。 主从复制提供副本,但复制本身不完成自动切换。单主仍能承接数据和写入压力时,可以由 Sentinel 协调故障转移;需要分片扩容时,Cluster 把槽交给多个主节点,并给每个主节点配置副本。
两种方案都需独立故障域、客户端拓扑刷新和重试处理。持久化及备份另按恢复目标配置,异步复制下不能承诺已确认写入零丢失。
9.2 Redis 分片和副本有什么区别?
考察重点:数据分布、读写容量与故障接管。
相关内容:主从、Sentinel 与 Cluster、副本与写入压力。
参考回答
分片把不同数据交给不同主节点,副本复制同一份数据。 分片可以分散数据容量和写入请求,但引入跨槽操作及迁移问题;副本可以用于故障接管或可接受延迟的读取,每个副本仍要应用该主节点的写入。
Cluster 可以同时使用两者:主节点之间分片,每个主节点各自有副本。仅有分片而没有副本时,故障分片没有现成节点接管。
9.3 Redis 主从怎样同步,副本落后时怎样处理?
考察重点:全量与部分重同步、偏移量及延迟处理条件。
相关内容:全量同步与部分重同步、复制延迟、复制确认与写入限制。
参考回答
副本先获得数据集,再持续应用主节点传播的修改;默认异步,所以可能落后。 断线恢复时,复制历史匹配且 backlog 覆盖缺口,就能部分重同步,否则要全量同步。
排查同一历史下的偏移差及变化趋势,再看网络、副本处理能力和全量加载等因素。持续处理能力不足不能只靠增大 backlog 解决。业务必须读到刚写入的值时要调整读取路径,WAIT 可要求副本确认,但超时不回滚,也不等于落盘或任意故障下的数据保证。
9.4 Redis Sentinel 如何完成故障转移?
考察重点:下线判定、执行者授权、副本选择及客户端发现。
相关内容:下线判定与切换授权、提升副本与客户端切换。
参考回答
Sentinel 先共同判断主节点故障,再授权一个 Sentinel 协调副本提升与配置变化。 单个 Sentinel 的判断是主观下线;达到 quorum 后是客观下线,真正执行切换还需多数授权及 quorum 要求。
执行者从健康且允许提升的副本中,依次按优先级、复制偏移量和运行 ID 选择新主,再让其他副本跟随它。客户端重新发现地址并切换连接。旧主恢复后作为副本加入,独有写入不会自动合并;请求结果未知时还要按业务幂等规则重试。
9.5 Redis Cluster 怎样组织数据,部署时需要考虑哪些条件?
考察重点:槽、主从关系、网络可达性和客户端路由。
相关内容:哈希槽与同槽操作、部署与故障转移、MOVED 与 ASK。
参考回答
Cluster 把键映射到 16384 个槽,再把槽分配给多个主节点,每个主节点可配自己的副本。 三主三副本是一种部署例子,仍需让主从分处合适的故障域,保证客户端、复制和集群总线连接可达。
客户端要支持槽路由、重定向和拓扑刷新。多键原子操作需满足同槽约束,新增节点后要实际分配和迁移槽;同时检查每个分片的负载、容量和可接管副本,不能只统计进程数量。
9.6 Redis 数据迁移需要处理哪些问题?
考察重点:迁移范围、增量追赶、切换入口及数据核对。
参考回答
先明确是整套实例迁移还是 Cluster 槽迁移,以及是否允许停写。 可以停写时,在停止写入后复制、恢复和核对;在线迁移则要衔接已有数据与新增修改,在最终切换点处理旧写入口,避免遗漏或分叉。
Cluster 槽迁移还涉及导出/导入状态、逐批迁键、临时 ASK 和最终槽归属变化。两种迁移都应核对键类型、值、TTL 和失败项,控制大 key 与迁移速度,并考虑切换后新写入使回退变复杂的问题。
9.7 Redis 已回复写入成功,主从切换后为什么仍可能丢失数据?
考察重点:异步复制窗口、接管节点及确认条件。
参考回答
主节点回复成功时,接管副本可能还没有收到这次写入。 如果主节点随后故障,提升的副本便缺少该修改。旧主开启 AOF,也不能证明新主拥有对应数据。
WAIT 和副本数量限制可以缩小风险窗口,但还要考虑确认到哪几个副本、实际提升哪个副本,以及这些副本是否又故障。不能把复制确认直接当作强一致提交或持久化保证。
9.8 Redis Cluster 有副本,就能在任意主节点故障后继续服务吗?
考察重点:合格副本、多数授权、槽覆盖和数据完整性。
参考回答
不能,只部署副本还不够。 故障主节点需要有可接管的合格副本,副本要获得持有槽的主节点多数授权,客户端也要能发现并访问新节点。
默认完整槽覆盖要求可能让一个失去负责节点的槽影响整个集群。关闭该要求只能在其他条件满足时服务仍可用的槽,不能让少数派无条件写入。成功接管后,数据完整性仍受异步复制进度约束。
10. 参考资料
- Redis:Replication
- Redis:High availability with Sentinel
- Redis:Cluster specification
- Redis 7.4.9:复制与 Cluster 配置
- Redis 7.4.9:Sentinel 配置
- Redis 7.4.9:复制实现
- Redis 7.4.9:Sentinel 下线判断、授权与副本选择
- Redis 7.4.9:槽路由与迁移实现
- Redis 7.4.9:Cluster 故障判定与选举实现
来源:Coding 101 原文 · 免费阅读,引用请保留出处。内容使用说明