1.背景
假设,当前redis集群只有2个节点 node-A 和 node-B,总共有3个slot, 其和节点的对应关系如图, client 会将其缓存一个映射map
现在要扩容新增一个节点 node-3, 并把 slot3 的数据迁移到 node-3,如下图
2. 问题
在 slot-3 迁移结束前,crc16(key) % 16384 = 3 的 key, 可能还在 node-B, 也可能迁移到 node-C 了
- 那么 client 是应该向 node-B 发送请求,还是向 node-C发送请求?
- 客户端的 slot -> node 的映射缓存什么时候更新?
3.redis cluster 中协议找答案
先看看 key 迁移过程中,redis cluster 节点会返回什么响应
假设slot-3 共有 key1,key2,key3,key4,key5 5个key,当前 key1、key2、key3 已经迁移到 node-C 了, 如下图:
3.1 操作还在原节点 node-B 的 key: key4
正常返回
3.2 操作已经迁移到新节点node-C 的 key: key1
(1) 由于key1已经迁移到 node-C了, 这个时候 node-B 会返回 -ASK 重定向到 node-C
(2) 接着需要先向 node-C 发送一个 ASKING 指令,然后才能从 node-C 获取到 key1 的值
(3) 如果直接向 node-C 发送 get key1 的话,会收到 moved 响应,让你去 node-B 执行;所以此时更新客户端 node映射缓存是没有用的
结论
在slot迁移过程中,任然向 slot 原节点发送命令,每次收到 ASK 后,先发送 ASKING 指令临时重定向到新节点,再正常执行命令, 此时不需要更新 客户端slot-node 映射缓存
扩展:ASK 和 MOVED 重定向的区别
- ASK 只是临时重定向,此时 slot 对应的数据在多个节点上,并不明确,redis-cluster还是将其归属在老节点;要先向 ASK 的新节点发 asking 后才能向新节点发送命令,下一次命令还是需要向老节点发送命令
- MOVED 是永久重定向,表示 slot 不在当前 redis 节点了,发错地方了 ,后续该slot的请求都应该发往 MOVED 重定向的 node, 如果客户端收到 MOVED 说明 slot-node的映射不对了,该执行 cluster slots 获取新的映射关系 更新本地缓存了
参考:https://redis.io/topics/cluster-spec#ask-redirection