涉及知识点
- 虚拟槽分区
- 客户端路由
1.moved重定向
2.ask重定向 - smart智能客户端
- 多节点命令实现
1.虚拟槽分区
虚拟槽分区是Redis Cluster采用的分区方式
预设虚拟槽,每个槽就相当于一个数字,有一定范围。每个槽映射一个数据子集,一般比节点数大
步骤:
1.把16384槽按照节点数量进行平均分配,由节点进行管理
2.对每个key按照CRC16规则进行hash运算
3.把hash结果对16383进行取余
4.把余数发送给Redis节点
5.节点接收到数据,验证是否在自己管理的槽编号的范围
如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果
如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中
需要注意的是:Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽
2.客户端路由
2.1 moved异常
2.2 ask异常
ask异常:客户端访问了正在迁移的键值对会得到 ask 异常,ask 异常会告诉访问的键值迁移到哪了,客户端再据此访问目标节点;
127.0.0.1:6380> keys *{info}*
1) "user:{info}:name"
2) "user:{info}:email"
3) "user:{info}:id"
4) "user:{info}:age"
127.0.0.1:6380> cluster keyslot *{info}*
(integer) 5642
相关命令
-
cluster keyslot hello: 获取"hello"这个key是哪个槽
-
cluster nodes:获取槽所在节点的信息,就可以知道键所在的节点信息。
-
redis-cli时,可以加上-c参数,这样redis会自动帮我们连接到正确的节点执行命令。
-
hash_tag: 如果键中包含{},则集群在计算槽时会使用{}内的内容,而不是整个键,{}内的内容又称为hash_tag。它提供不同的键拥有相同的slot功能,通常用于redis IO优化。
-
CLUSTER SETSLOT [slot] migrating [destination-node-id]
-
CLUSTER SETSLOT [slot] importing [source-node-id]
-- 接收节点执行 cluster setslotimporting <node_id> (node_id为源节点id)
-- 来源节点执行 cluster setslotmigrating <node_id> (node_id为接收节点id)
分布式下集群一致性问题:
参考:Redis Cluster 集群一致性原理及slot迁移测试
在使用gossip协议中, 如果多个节点声称不同的集群信息, 那对于某个节点来说究竟要相信谁呢? Redis Cluster规定了每个主节点的epoch都不可以相同. 而一个节点只会去相信拥有更大node epoch的节点声称的信息, 因为更大的epoch代表更新的集群信息.
原则上:
(1)如果epoch不变, 集群就不应该有变更(包括选举和迁移槽位)
(2)每个节点的node epoch都是独一无二的
(3)拥有越高epoch的节点, 集群信息越新
slot 管理
首先我们搞清楚slot究竟是怎么管的. 每个节点都有一份16384长的表对应每个slot究竟归哪个节点, 并且会保存当前节点所认为的其它节点的node epoch. 这样每个slot实际上绑定了一个节点及其node epoch. 然后由自认为拥有某slot的节点来负责通知其它节点这个slot的归属. 其它节点收到这个消息后, 会对比该slot原先绑定节点的node epoch, 如果收到的是更大的node epoch则更新, 否则不予理睬. 除此之外, 除了使用slot相关命令做变更, 集群没有其它途径修改slot的归属.
slot x 是我管的, 我的node epoch是 y
node A ------------------------------> node B
(原来slot x归node C管, 如果 y 比 node C 的node epoch大, 我就更新slot x的归属)
这实际上依赖上述的原则(3), 并且相信slot的旧主人还没有更新epoch.
moved 和 ask
- 两者都是客户端重定向;
- moved 异常说明槽已经确定迁移;
- ask 异常说明槽还在迁移中,客户端访问的键有可能在 source 节点,有可能在 target 节点;(因为一个槽可能对应多个key,所以迁移一个槽的时候,一部分key在source,一部分key在target)
两种异常对客户端造成的挑战
- 怎么一下就命中键值真实存在的节点;
- 怎么保证性能?