一、什么是 Redis 分片集群
Redis 分片集群通常指 Redis Cluster。
它的主要作用是:
-
将数据分散到多个 Redis 主节点上。
-
突破单台 Redis 的内存容量限制。
-
提高整个集群的读写能力。
-
配合主从复制实现高可用。
典型结构:
master A master B master C
| | |
replica A replica B replica C
其中:
-
多个 master 保存不同的数据,负责数据分片。
-
replica 复制对应 master 的数据,负责故障恢复。
因此:
Redis Cluster = 数据分片 + 主从复制 + 故障转移
二、什么是哈希槽
Redis Cluster 将整个数据空间划分为:
16384 个哈希槽
槽编号范围:
0 ~ 16383
槽不是实际的内存块,也不会提前占用固定大小的内存。
槽本质上只是一个逻辑编号,用来对 key 进行分组和路由。
例如:
槽 100:可能没有 key
槽 200:可能有 1 个 key
槽 300:可能有 10000 个 key
因此,槽的数据量并不固定。
三、key 如何确定所属槽
Redis 会根据 key 计算槽号:
slot = CRC16(key) % 16384
例如:
user:1 → 槽 3000
shop:1 → 槽 7000
order:1 → 槽 13000
只要 key 的内容不变,按照同样的计算规则,它对应的槽号就不会改变。
完整关系:
key
↓ 计算
槽号
↓ 查询归属
Redis master 节点
例如:
user:1
↓
槽 3000
↓
master A
所以数据不是直接根据 key 分配给节点,而是经过一层槽:
key → 槽 → 节点
四、槽和数据的关系
槽是对数据的逻辑分组,不是实际保存数据的地方。
真正的数据仍然保存在 Redis 节点自己的内存中。
例如:
槽 0~5000 → master A
槽 5001~10000 → master B
槽 10001~16383 → master C
如果:
user:1 → 槽 3000
那么 user:1 就实际保存在 master A 的内存中。
可以理解为:
槽属于哪个 master
↓
该槽中的 key 就存在哪个 master
五、为什么需要槽
如果不使用槽,而直接按照节点数量计算:
节点 = hash(key) % 节点数量
原来有 3 个节点:
hash(key) % 3
扩容为 4 个节点后:
hash(key) % 4
因为取模数字发生变化,大量 key 的计算结果都会改变,因此可能需要迁移大量数据。
使用固定的 16384 个槽后:
key → 槽
这个映射基本不变。
扩容或缩容时只需要修改:
槽 → 节点
然后迁移这些槽中的数据。
因此,槽的核心作用是:
将 key 的分布规则与 Redis 节点数量分离。
六、扩容时槽如何迁移
假设原来:
槽 5000 → master A
槽 5000 中的数据也都存放在 master A。
现在增加 master B,并准备将槽 5000 交给 B。
不能只修改槽的归属,而不迁移数据。
正确过程是:
1. 将槽 5000 标记为正在从 A 迁移到 B
2. 将槽 5000 中的 key 从 A 逐个迁移到 B
3. 数据迁移完成
4. 正式将槽 5000 的归属修改为 B
最终结果:
迁移前:
槽 5000 → master A
数据 → master A
迁移后:
槽 5000 → master B
数据 → master B
所以扩容时真正需要处理两件事:
修改槽归属
+
迁移槽中的实际数据
七、迁移过程中数据在哪里
迁移槽不是直接搬运一整块内存,而是逐个迁移槽中的 key。
例如槽 5000 中有:
key1
key2
key3
迁移过程中可能出现:
key1 已经迁移到 B
key2 仍然留在 A
key3 仍然留在 A
因此,迁移过程中数据可能暂时同时分布在 A 和 B。
Redis 会通过迁移状态和 ASK 重定向保证请求能够找到正确的数据。
迁移完成后:
槽 5000 中的所有 key 都在 B
槽 5000 正式归 B 管理
正常情况下不会长期存在:
槽归 B 管理
数据却一直留在 A
因为 Redis 节点只能直接管理自己进程内存中的数据,B 不能直接管理 A 内存中的 key。
八、ASK 和 MOVED
1. ASK
槽正在迁移时,如果客户端访问了已经被迁移到新节点的 key,旧节点会返回:
ASK 槽号 新节点地址
含义:
该槽还在迁移,这一次请求暂时去新节点执行。
ASK 是临时重定向。
2. MOVED
槽已经正式归新节点管理后,客户端访问旧节点会收到:
MOVED 槽号 新节点地址
含义:
该槽已经正式属于新节点,以后都应该访问新节点。
MOVED 是正式重定向。
九、不同 key 存在不同服务器怎么办
这是分片集群的正常情况。
例如:
user:1 → master A
shop:1 → master B
order:1 → master C
客户端会:
1. 根据 key 计算槽号
2. 查询该槽由哪个节点负责
3. 将请求发送给正确节点
通常由 Lettuce、Jedis、Redisson 等集群客户端自动完成。
因此单 key 操作一般没有问题:
GET user:1
SET shop:1 value
HGET order:1 status
客户端会自动将不同请求发送给不同节点。
十、多 key 操作的问题
如果一条命令同时操作多个 key,而这些 key 属于不同槽,就可能报错:
CROSSSLOT Keys in request don't hash to the same slot
例如:
MGET user:1 shop:1
假设:
user:1 → master A
shop:1 → master B
一条 Redis 命令无法同时在两个节点上原子执行。
受影响的操作包括:
MGET
MSET
DEL 多个 key
事务
Lua 脚本
集合交集、并集、差集
十一、Hash Tag
为了让相关 key 进入同一个槽,可以使用 {}。
例如:
user:{1}:name
user:{1}:age
user:{1}:orders
Redis 计算槽位时,只计算大括号中的内容:
1
因此这些 key 一定属于同一个槽,也会存放在同一个 master 节点上。
例如:
seckill:{10}:stock
seckill:{10}:order
这两个 key 都使用 {10} 计算槽号,因此可以在同一个 Lua 脚本中操作。
但是:
user:{1}:name
user:{2}:name
大括号内容不同,仍然可能进入不同槽。
十二、核心理解
Redis Cluster 中有两层映射:
第一层:key → 槽
第二层:槽 → master 节点
其中:
key → 槽
由固定的哈希规则决定,只要 key 不变,槽号通常就不变。
而:
槽 → 节点
可以随着扩容、缩容、故障转移而改变。
当槽的归属改变时,必须将槽中的实际数据迁移到新的节点。
最终可以记住这句话:
key 决定自己属于哪个槽,集群决定槽属于哪个 master;槽归属发生变化时,槽中的数据也必须迁移到新的 master。