核心目标:理解 16384 个哈希槽的分片原理、gossip 通信与 MOVED/ASK 重定向;能搭建三主三从集群;理解集群模式的功能限制(CROSSSLOT、db0 only、多 key 命令);能评估"哨兵 + 主从"与"集群"的选型。
前置知识 :完成 Part 10(复制与故障转移------集群分片内仍依赖这些机制)。
验证环境 :Redis 8.10.0(cygwin 移植版);三主三从共 6 个隔离实例(6380-6385),
redis-cli --cluster创建。最后复核日期:2026-08-07。
0. 本篇问题场景:单主库的天花板
哨兵解决了"主库挂了自动切换",但仍有三个上限:
- 写容量:所有写都打在一个主库,单线程 CPU 饱和后加机器没用;
- 内存容量:数据总量受单机内存限制,换大内存机器有成本上限;
- 故障半径:一次主库宕机,整个数据集不可写。
Redis Cluster 的答案:分片------把 16384 个哈希槽摊到多个主节点,每个主节点只服务自己那部分槽。写能力、内存容量、故障半径随之分摊。
1. 分片模型:16384 个哈希槽
1.1 槽的分配
text
CLUSTER KEYSLOT order:1001 → slot 241 (节点 A)
CLUSTER KEYSLOT user:42 → slot 15880 (节点 C)
CLUSTER KEYSLOT product:9 → slot 264 (节点 A)
本实验三主节点分片(redis-cli --cluster create 自动分配):
text
127.0.0.1:6380 master slots:[0-5460]
127.0.0.1:6381 master slots:[5461-10922]
127.0.0.1:6382 master slots:[10923-16383]
+ 6383/6384/6385 分别为三个主的从库
cluster info 的健康指标:
text
cluster_state:ok
cluster_slots_assigned:16384 ← 槽全部有归属
cluster_known_nodes:6 ← 主从共 6 个节点
1.2 槽的计算:CRC16
text
slot = CRC16(key) % 16384
16384 是固定值(不是节点数!)------槽是"逻辑位置",节点是"物理位置",两者解耦后,扩容/缩容只需要迁移槽,不需要重新哈希全部数据。
2. 请求路由:MOVED 与 ASK
2.1 MOVED:槽在别人那
客户端直连节点 A 请求一个属于节点 C 的 key,节点 A 返回 MOVED:
text
$ redis-cli -p 6380 GET user:42
MOVED 15880 127.0.0.1:6382
MOVED 15880 127.0.0.1:6382 = "slot 15880 在 6382,去那里"。智能客户端(redis-py 的 RedisCluster、redis-cli -c)会缓存槽-节点映射并自动重定向:
text
$ redis-cli -c -p 6380 SET user:42 "alice"
OK
$ redis-cli -c -p 6380 GET user:42
alice
2.2 ASK:槽正在迁移
槽迁移(扩容/缩容)期间,部分 key 已搬到目标节点。源节点对已搬走的 key 返回 ASK------与 MOVED 的区别:
| MOVED | ASK | |
|---|---|---|
| 含义 | 槽已永久属于对方 | 槽正在迁移,这个 key 暂时在对方 |
| 客户端处理 | 更新槽映射缓存 | 只重定向这一次,不更新缓存 |
text
ASK 15880 127.0.0.1:6382
2.3 集群模式的功能限制
单节点可以跨 key 操作(MGET、事务、Lua),但槽不同就报 CROSSSLOT:
text
$ redis-cli -p 6380 MGET order:1001 user:42
(error) CROSSSLOT Keys in request don't hash to the same slot
集群模式下的限制汇总:
| 能力 | 限制 |
|---|---|
| 多 key 命令(MGET/MSET/SUNION...) | 所有 key 必须同槽 |
| MULTI 事务 / Lua 脚本 | 涉及的 key 必须同槽 |
SELECT |
只有 db 0 |
| 管道/发布订阅 | 部分支持,跨槽受限 |
| 单 key 大对象 | 不受限,但会倾斜(Part 4 大 key 危害放大) |
2.4 hash tag:把相关 key 钉进同一槽
官方 7.4 支持 hash tag :key 中 {...} 部分参与哈希,其余忽略。user:{42}:name 与 user:{42}:cart 同槽,从而支持同槽 MGET/事务/Lua。
⚠️ 本机 8.10.0(cygwin 移植版)实测 hash tag 未生效 :
CLUSTER KEYSLOT "user:{42}:name"= 6755、"user:{42}:cart"= 12984,不同槽!官方实现(cluster.c的keyHashSlot)应取{}内子串做 CRC16,本机移植版行为不符。该结论仅基于本机 8.10.0 移植版实测,标准发行版请在目标环境用CLUSTER KEYSLOT复核。工程教训 :hash tag 是"跨 key 操作"的前提,上线前必须用
CLUSTER KEYSLOT实测确认 tag 生效,不能默认所有发行版行为一致。若 tag 不可用,跨 key 业务要么改单实例,要么应用层聚合。
3. 主从与故障转移
集群的每个分片仍是一主一从(复制机制同 Part 10)。从库不参与分片,只做副本:
- 主节点宕机 → 从库通过 gossip 检测(
cluster-node-timeout,默认 15 秒)→ 从库自动提升为新主(无需哨兵!哨兵是集群外的独立组件,集群自带选举); - 数据与角色转移演示(本实验手动触发
CLUSTER FAILOVER):
text
$ redis-cli -p 6383 CLUSTER FAILOVER # 6383 是 6380 的从
OK
$ redis-cli -p 6383 cluster nodes
127.0.0.1:6383@16383 myself,master ← 从库接管成为主
$ redis-cli -p 6383 cluster info
cluster_state:ok ← 集群仍健康
$ redis-cli -c -p 6383 GET order:1001
paid ← 数据可继续访问
自动故障转移同样有"检测时间 + 选举"的不可用窗口(默认 15 秒级),且部分分片宕机时,只有该分片的槽不可用------这是集群相对哨兵(整个数据集)的故障半径优势。
4. 扩容缩容:槽迁移
扩容 = 加节点 → 从现有主节点迁出部分槽 (reshard):
text
# 交互式迁移(或 --cluster-from/--cluster-to 指定源与目标)
redis-cli --cluster reshard 127.0.0.1:6380 \
--cluster-from <源节点id> --cluster-to <新节点id> \
--cluster-slots 1000 --cluster-yes
迁移期间访问中的 key 返回 ASK (§2.2),客户端需要处理。缩容同理反向迁移。槽迁移是数据拷贝,期间有内存/带宽开销------扩容要在低峰期做,且新节点先以"空槽主节点"加入。
5. 选型:哨兵 + 主从 vs 集群
| 维度 | 哨兵 + 主从 | Redis Cluster |
|---|---|---|
| 数据容量 | 单主上限 | 横向扩展(多分片) |
| 写能力 | 单主上限 | 随分片增长 |
| 故障半径 | 整个数据集切换 | 仅宕机分片 |
| 复杂度 | 低(哨兵配置 + 客户端) | 高(槽、重定向、跨槽限制) |
| 跨 key 操作 | 无限制 | 受 CROSSSLOT 限制 |
| 适用 | 数据量 < 单机、写 QPS 可控 | 数据量/写 QPS 超单机 |
结论 :能单机就单机,需要高可用加哨兵;只有当单机内存或写吞吐成为瓶颈时再上集群------集群的跨槽限制会反过来约束业务(多 key 操作、事务、Lua)。
6. 版本与环境差异
| 差异点 | 官方 7.4 | 本机 8.10.0(cygwin 移植版) |
|---|---|---|
| 槽分配/重定向 | 一致 | 一致(MOVED/ASK 实测正常) |
| hash tag | keyHashSlot 支持 {} |
实测未生效(§2.4)------上线前必须实测 |
| 故障转移 | gossip + 从库提升 | 一致 |
| 集群命令 | redis-cli --cluster |
可用 |
7. 测试与验收
- 集群测试(隔离环境):槽全覆盖(16384)、MOVED/ASK 重定向、CROSSSLOT 拒绝、
CLUSTER FAILOVER后数据可访问; - hash tag 验收:
CLUSTER KEYSLOT实测{tag}:a与{tag}:b同槽(本机环境不满足!)。
本篇验收清单:
- 能解释"16384 槽 vs 节点数"的解耦设计(扩容只需迁槽);
- 能区分 MOVED(永久归属)与 ASK(迁移中临时指向);
- 能说出集群模式下跨 key 操作的限制(CROSSSLOT)与 hash tag 的作用;
- 知道集群自带的从库提升机制与哨兵的差异;
- 能在"哨兵 + 主从"与"集群"之间给出选型依据;
- 搭建过三主三从集群并完成一次接管演练。
8. 常见误区
- "集群 = 多台机器各存各的" ------是槽分片不是节点分片,节点只是槽的宿主;扩容迁移的是槽。
- "集群可以替代哨兵"------集群自带故障转移,但跨 key 限制让很多业务无法直接迁移;小数据量上集群得不偿失(§5)。
- "hash tag 所有版本都支持" ------本机 8.10 移植版实测不生效;必须
CLUSTER KEYSLOT验证(§2.4)。 - "MOVED 和 ASK 一样"------MOVED 永久更新路由缓存,ASK 只重定向单次(§2.2)。
- "集群里随便 MGET"------跨槽报 CROSSSLOT;需要同槽(hash tag)或应用层聚合。
9. 本篇小结
- 分片:16384 槽解耦"逻辑位置"与"物理节点",扩容=迁槽;
- 路由:MOVED(槽迁移后)/ ASK(槽迁移中)两种重定向;
- 限制 :跨槽多 key 操作被 CROSSSLOT 拒绝,hash tag 是解药但必须实测;
- 选型:单机 → 哨兵 → 集群,逐级升级,集群的复杂度要用"确实超单机"来换取。
至此高可用三件套(复制、哨兵、集群)讲完。最后一篇 Part 12:性能、观测与生产排障 把整条知识链收拢到"问题怎么查":慢查询、延迟、大 key、热 key、连接治理与排障决策树。
10. 官方资料
- Redis Cluster 文档:https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/
- Cluster 规范(槽、重定向、hash tag):https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/
CLUSTER命令:https://redis.io/docs/latest/commands/cluster/- redis-py cluster:https://redis-py.readthedocs.io/en/stable/commands.html#redis-cluster