Redis 最佳实践(四):部署架构、Cluster 扩缩容与高可用
1. 架构选择
Redis 的部署形态解决不同问题:
| 架构 | 数据分片 | 自动故障转移 | 容量扩展 | 主要限制 |
|---|---|---|---|---|
| 单机 | 无 | 无 | 垂直扩容 | 单点故障,容量和吞吐受单节点限制 |
| 主从 | 无 | 无(需人工或外部系统) | 可扩展部分只读流量 | 异步复制,读可能旧,主库仍是写瓶颈 |
| Sentinel | 无 | 有 | 可扩展部分只读流量 | 数据集仍在单主节点,客户端需支持 Sentinel |
| Redis Cluster | 16384 槽 | 有 | 水平扩容量和写吞吐 | 跨槽多 Key、运维和客户端更复杂 |
选择原则:
单主节点能否在安全水位承载未来 12~18 个月的数据、写入和带宽?├─ 能│ ├─ 是否需要自动高可用?是 → 主从 + Sentinel│ └─ 否 → 单机/主从,但生产通常仍建议有副本与备份└─ 不能 → Redis Cluster ├─ 业务是否大量依赖跨 Key 原子操作? │ ├─ 是 → 重构 Key 边界、Hash Tag,或重新评估架构 │ └─ 否 → 按槽水平拆分 └─ 客户端、监控、迁槽、备份和演练是否已就绪?不要仅用“Redis 单机能跑多少 QPS”做决策。实际瓶颈可能是内存、BigKey、网络带宽、持久化、命令复杂度、故障恢复时间或单线程 CPU。
2. 主从与 Sentinel 基础
2.1 异步复制语义
client → primary:写入并收到确认primary → replica:异步传播命令主节点确认写入时,副本可能尚未收到。因此主节点突然故障并切换副本时,少量已确认写可能丢失。WAIT/WAITAOF 可提高特定写的复制/持久化确认程度,但不能把 Redis 变成网络分区下的强一致分布式数据库。
2.2 读副本
读写分离适合:
- 允许短暂旧读的查询;
- 报表、推荐等可容忍复制延迟的流量;
- 大读取需要与主库隔离。
不适合:
- 写后必须立即读到新值;
- 库存、余额、锁等强一致判断;
- 依赖副本作为零延迟灾备。
应用必须监控复制延迟,并在副本落后时降级或切回主库。主节点的网络和 CPU 仍承担复制成本,增加副本并非免费扩容。
2.3 Sentinel 注意事项
- 推荐部署奇数个 Sentinel,并分布在独立故障域;
- Sentinel 数量和
quorum不是同一概念;故障确认与选举还涉及多数派; - 客户端必须支持 Sentinel 地址发现和主节点切换;
- 故障转移期间会有短暂不可用,应用需设置有界重试和退避;
- 旧主恢复后应作为副本重新加入,防止双主写入;
- Sentinel 不做数据分片,无法解决单主内存或写吞吐上限。
3. Redis Cluster 核心模型
3.1 16384 个槽
slot = CRC16(key) mod 16384
master A:slots 0~5460master B:slots 5461~10922master C:slots 10923~16383Key 映射到槽,槽再归属于主节点。扩缩容时移动的是槽及槽中的 Key,而不是简单修改客户端一致性哈希环。
3.2 Hash Tag
Key 中第一对有效非空 {...} 的内容用于计算槽:
order:{1001}:detailorder:{1001}:items它们同槽,可用于同槽多 Key 操作、事务或脚本。Hash Tag 应表达最小原子聚合边界,例如订单 ID 或用户购物车 ID。
错误做法:
# 全租户共用一个 tag,导致所有数据落在一个槽tenant:{t1}:user:1tenant:{t1}:order:1tenant:{t1}:product:13.3 节点通信
Cluster 节点之间形成全连接拓扑,并通过集群总线和 gossip 交换状态。增加节点会增加:
- 节点连接数;
- 心跳/gossip 流量;
- 故障检测和配置传播复杂度;
- 运维、升级、备份和告警对象数量。
官方规范以约 1000 节点作为设计规模量级,不代表实际生产应接近该数字。大多数团队应使用远少于此的节点,并通过压测、故障域和运维能力确定上限。
4. 多 Key、事务和脚本
原课件中“Cluster 不支持 Lua 和事务”的说法不准确。准确结论是:
- 单 Key 命令可正常路由;
- 多 Key 命令在所有 Key 同槽时可执行;
MULTI/EXEC、Lua/Function 所访问的 Key 通常必须同槽;- 跨槽原子事务不是 Redis Cluster 的通用能力;
- 客户端将跨槽操作拆组后,只能得到各组独立成功/失败的最终一致语义。
设计方法:
| 需求 | 方案 |
|---|---|
| 同一订单内多个 Key 必须原子 | {orderId} Hash Tag 共槽,控制订单规模 |
| 大量独立 Key 批量读取 | 按槽/节点分组并行,重组结果 |
| 跨业务域强事务 | 权威数据库完成事务,Redis 做缓存/派生数据 |
| 跨槽最终一致更新 | 消息、任务表、幂等 requestId 和补偿 |
5. 完整覆盖与部分可用
cluster-require-full-coverage 决定部分槽不可用时集群如何响应:
| 配置 | 行为取向 | 适合思路 |
|---|---|---|
yes | 只要存在未覆盖槽,整个集群停止接受普通查询 | 业务要求完整 Key 空间语义,宁可整体不可用 |
no | 仍允许访问有可用槽的数据 | 分区业务可独立降级,接受部分 Key 空间不可用 |
不能统一推荐 no。例如一个请求同时依赖用户、库存和订单三个槽,部分返回可能比整体失败更危险。选择前必须明确:
- 客户端能否识别并处理部分不可用;
- 上层接口是否允许返回不完整数据;
- 是否会把缺失误判为“不存在”;
- 恢复后是否有对账/补偿。
6. 容量规划
6.1 单分片安全水位
按最忙分片而不是集群平均值规划:
分片内存预算= 数据集+ Key/对象开销+ 过期与淘汰波动+ 客户端/复制/AOF 缓冲+ allocator 碎片+ fork COW 峰值+ 扩缩容迁移余量“每个实例 4 GB 或 8 GB”只是历史经验,不是限制。实例过大可能导致:
- fork、RDB/AOF、全量同步和重启更慢;
- 节点故障迁移的数据更多;
- COW 峰值更难预留;
- 迁槽和备份恢复时间更长。
实例过小则会导致节点太多、gossip 和连接开销高、运维复杂。应在故障恢复时间和节点数量之间压测取舍。
6.2 内存倾斜
槽位数量均匀不等于数据均匀:
节点 A:5461 个槽,但包含多个超大 Key节点 B:5461 个槽,都是小 Key因此重平衡需要看:
- 每节点内存、Key 数、QPS、网络;
- 每槽 Key 数和内存分布;
- BigKey/HotKey 所在槽;
- 写入增长速度;
- 副本承载能力。
6.3 网络带宽
Redis 可能先耗尽网络而不是 CPU:
业务响应流量+ 客户端请求流量+ 主从复制+ Cluster bus+ 迁槽/全量同步+ 备份流量扩容和故障恢复期间,复制/迁移流量会与在线业务竞争。必须限速、分批并在低峰执行,同时保留故障切换带宽。
7. 槽位与负载均衡
7.1 倾斜来源
- Hash Tag 粒度过大;
- 少量 BigKey 占据大量内存;
- HotKey 将 QPS 集中到单槽;
- 业务 ID 分布或 Key 命名异常;
- 扩容后只移动相同数量槽,未按数据/流量再平衡;
- 单个客户端错误路由或连接池倾斜。
7.2 治理方式
| 倾斜 | 处理 |
|---|---|
| 内存倾斜 | 拆 BigKey、迁槽、重新分配槽 |
| QPS/网络倾斜 | 拆 HotKey、客户端本地缓存、热点副本、业务分流 |
| 大 Tag 倾斜 | 缩小原子边界,重新设计 Tag |
| 节点规格不一致 | 统一规格,或有意识按能力分配槽并纳入自动化 |
单纯迁移 HotKey 所在槽只是把热点从 A 搬到 B,不会消除单节点瓶颈。
8. 扩容与缩容
8.1 扩容流程
1. 校验新节点版本、配置、安全和资源2. 将新主节点加入 Cluster3. 为主节点配置副本并跨故障域放置4. 小批次迁移槽,限制迁移速率5. 观察 P99、网络、复制、错误和重定向6. 逐步完成重平衡7. 校验 16384 槽覆盖、节点状态和数据分布迁槽期间客户端可能收到 MOVED 或 ASK。必须使用支持 Cluster 拓扑发现、重定向和连接复用的客户端,并验证版本兼容性。
8.2 缩容流程
1. 确认剩余节点容量与故障余量2. 把待下线主节点的所有槽迁出3. 校验该节点不再持有槽/业务 Key4. 调整或移除副本关系5. 从 Cluster 忘记并下线节点6. 更新监控、备份、服务发现和资产记录缩容最容易忽略故障余量:正常状态能装下数据,不代表再故障一台时仍能运行。
9. 高可用与故障转移
9.1 故障域
每个主节点的副本应尽量分布在不同:
- 物理机;
- 机架;
- 可用区;
- 电源/网络故障域。
“每主一副本”是常见起点,但不是所有 SLA 的答案。需要考虑同时故障、维护窗口和副本迁移。
9.2 数据丢失窗口
Redis Cluster 使用异步复制。以下情况可能丢失已确认写:
- 主节点确认后、复制前故障;
- 客户端与少数派主节点继续通信;
- 故障检测和网络分区窗口;
- 故障转移选中的副本落后。
可降低风险的手段:
- 合理配置
min-replicas-to-write和min-replicas-max-lag; - 对关键写使用
WAIT/WAITAOF并理解其边界; - 监控复制 offset 和 lag;
- 把真正不能丢的事务记录在更合适的权威系统;
- 做网络分区和故障切换演练。
这些手段通常以可用性或延迟为代价。
9.3 脑裂与网络分区
Cluster 会在多数主节点可达、故障主节点有可用副本时进行故障转移。客户端不应固定写某台 IP;要通过 Cluster-aware 客户端刷新拓扑。分区恢复后,旧主的未同步写可能被丢弃,因此业务不能假设所有返回成功的写最终都保留。
10. 客户端最佳实践
10.1 必备能力
- 支持
MOVED、ASK重定向; - 自动发现并定期刷新槽位映射;
- 对每节点使用有界连接池;
- 拓扑变化时避免同时大量重连;
- 超时、重试、退避和最大重定向次数可配置;
- 多 Key 操作在发送前能检测同槽;
- 支持 TLS、ACL 用户名/密码和证书轮换;
- 暴露按节点、命令、异常类型的指标。
10.2 重试原则
| 情况 | 建议 |
|---|---|
MOVED | 更新槽映射并重试到目标节点 |
ASK | 临时向目标节点发送 ASKING 后执行,不永久改映射 |
| 连接失败 | 有界重试、指数退避、熔断 |
| 超时且写状态未知 | 仅自动重试幂等操作;非幂等写需业务去重 |
CLUSTERDOWN/槽不可用 | 快速失败或降级,不要无限重试放大故障 |
10.3 不要缓存过久的拓扑
扩缩容、故障转移和迁槽会改变槽归属。客户端如果只在启动时加载一次拓扑,会产生大量重定向甚至访问旧节点。应允许定期刷新、被动根据 MOVED 刷新,以及运维触发刷新。
11. Cluster 运维
11.1 常用检查
CLUSTER INFOCLUSTER NODESCLUSTER SHARDSCLUSTER SLOTSCLUSTER KEYSLOT some:keyCLUSTER COUNTKEYSINSLOT 1234INFO replicationINFO memory不同命令的推荐程度和输出会随版本变化。自动化解析应锁定版本并有兼容测试,不要依赖人为拼接文本的稳定列位置。
11.2 监控表
| 类别 | 重点 |
|---|---|
| 集群状态 | cluster_state、已分配/失败槽、已知节点 |
| 节点 | 主从角色、flags、ping/pong、连接状态 |
| 重定向 | MOVED/ASK 数量、拓扑刷新频率 |
| 复制 | lag、offset、全量/部分同步、backlog |
| 容量 | 每节点 used_memory、RSS、Key 数、增长率 |
| 性能 | 每节点 CPU、P99、命令量、网络吞吐 |
| 数据分布 | 槽内存、BigKey、HotKey、Hash Tag 倾斜 |
| 后台任务 | RDB、AOF rewrite、迁槽、fork/COW |
11.3 运维动作隔离
避免同时进行多个重负载动作:
迁槽 + AOF rewrite + 全量复制 + 备份它们会竞争 CPU、内存、磁盘和网络。编排系统应有互斥或并发上限,并在指标异常时自动暂停,而不是只按固定速率推进。
12. 备份、恢复与升级
12.1 备份
Cluster 的数据分布在多个主分片,备份必须覆盖所有分片并记录拓扑。各分片快照时间可能不同,因此它不天然是跨分片一致性快照。若业务要求全局一致恢复,需要应用级停写、时间点协议或平台提供的能力。
12.2 恢复
演练应验证:
- 所有分片文件可读;
- Redis 版本和模块兼容;
- 槽位映射正确;
- ACL、TLS、持久化和内核参数正确;
- 应用能刷新拓扑并恢复流量;
- Key 数、抽样数据和业务对账通过;
- RTO 达标。
12.3 滚动升级
通常先升级副本,验证后故障转移,再处理原主节点。升级前确认:
- RDB/AOF 格式和配置兼容;
- 客户端支持目标版本;
- 脚本、模块和命令无兼容问题;
- 新旧版本混跑窗口符合官方支持;
- 有明确回滚方案,但数据文件降级兼容不能想当然。
13. 是否真的需要 Cluster
优先使用 Sentinel 而非 Cluster 的常见条件:
- 单主节点能满足容量、写 QPS、网络和恢复时间;
- 业务大量使用多 Key 原子操作、事务或脚本;
- 团队尚无 Cluster 客户端和运维能力;
- 扩展需求主要是可容忍旧读的查询。
应考虑 Cluster 的常见条件:
- 数据集超过单节点安全内存水位;
- 写吞吐、主线程 CPU 或网络需要水平扩展;
- 单节点备份/恢复和故障转移时间不可接受;
- 数据可以按 Key 边界自然分区;
- 团队能够承担槽位、迁移、客户端和多分片运维。
架构越复杂,理论容量越大,但故障模式也越多。能用简单架构稳定满足目标时,不应只因“以后可能增长”过早上 Cluster;一旦增长预测和演练证明单机不可持续,也不能等到满载后才迁移。














