Redis 集群模式(Cluster)原理详解

avatar 2022年9月23日18:18:37 评论 237 次浏览

1、简述

Redis Cluster 集群中每个节点负责整个集群的一部分数据,每个节点上的数据多少可能不一样,节点之间通过一种特殊的二进制协议交互集群信息。  Redis Cluster将所有数据划分为16384个槽位,每个节点负责其中一部分槽位,槽位信息存储于每个节点中,当redis客户端来连接集群时,也会得到一份集群的槽位配置信息,这样当客户端要查找某个key时,可以直接定位到目标节点。  客户端为了可以直接找到具体key所在的节点,需要缓存槽位相关信息,这样就可快速定位到相对应的节点,同时可能存在客户端与服务器存储槽位信息不一致的情况,故需要纠正机制来实现槽位信息的校验调整。

2、槽位定位算法

Redis Cluster默认会对key值使用crc16算法进行hash得到一个整数值,然后用该整数值对16384进行取模来得到具体槽位。Redis Cluster还允许用户强制把某个key挂在特定槽位上,通过key字符串里面嵌入tag标记以此来强制key所挂的槽位等于tag所在的槽位。

3、跳转机制

当客户端向一个错误节点发送指令后,该节点会发现指令的key所在槽位并不归自己管,此时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连接该节点以获取数据。客户端在收到MOVED指令后,要立即纠正本地槽位映射表,后续所有key将使用新的槽位映射表

4、key 迁移(redis-trib为例)

Redis迁移的单位是槽,即一个槽一个槽的迁移,当一个槽正在迁移时,这个槽就处于中间过渡状态。如图,这个槽在源节点的状态为migrating,在目标节点的状态为importing,表示数据正在从源节点流向目标节点。

redis-trib会在源节点和目标节点设置好中间状态,然后一次性获取元节点槽位的多有key列表(keysinslot指令,也可部分获取),然后挨个key进行迁移。每个key的迁移过程以源节点作为目标节点的“客户端”,源节点对当前key执行dump指令得到序列化内容,然后通过“客户端”向目标节点发送restore指令携带序列化的内容作为参数,目标节点在进行反序列化将内容恢复到目标节点内存中,然后返回“客户端”ok,源节点“客户端”收到后再把当前节点的key删掉就完成了单个key迁移的全过程。  迁移过程是同步的,在目标节点执行restore指令到源节点删除key之间,源节点的主线程会处于阻塞状态,直到key被成功删除。  迁移过程中,如果每个key的内容都很小,那么migrate过程会很快,几乎不会影响客户端的正常访问,如果key的内容很大,因为migrate过程是阻塞的,所以会导致源节点和目标节点 卡顿,影响集群稳定性,所以要避免大key。

5、容错

当主节点发生故障,集群会自动将从节点提升为主节点,如果主节点没有从节点,则发生故障时,集群不可用。同时Redis也提供了一个参数 cluster-require-full-coverage 可以允许部分节点发生故障,其余节点可正常提供服务。

6、网络抖动

由于网络抖动问题导致主从频繁切换,Redis Cluster 提供 cluster-node-timeout 参数,当某个节点持续时间timeout时间失联时,才可以认定该节点出现故障,需要进行主从切换。同时参数 cluster-slave-validity-factory 作为倍乘系数放大这个超时时间来宽容容错的紧急程度,系数为零,主从切换不会抗拒网络抖动,系数大于1则就成了主从切换的松弛系数。

7、可能下线(PFail)与确定下线(Fail)

因为Redis Cluster是去中心化的,一个节点认为某个节点失联了并不代表所有节点都认为它失联了,所以集群会通过以此协商过程,只有当大多数节点认为某个节点失联了,那么该节点才需要进行主从切换来容错。  Redis 集群节点采用Gossip协议来广播自己的状态以及来改变对整个集群的认知。当一个节点发现某个节点失联后(Pfail),它会将该失联信息广播到整个集群,其他节点就会收到节点失联信息,如果收到某个节点失联的节点数量(PFail Count)已经达到集群的大多数,则可以标记该失联节点为确定下线状态(Fail),然后广播到其它节点该节点确定下线的事实,并立即对该失联节点进行主从切换。

8、槽位迁移感知

客户端保存了槽位与节点的映射关系表,它需要及时得到更新,才可以正常地将某条指令发送到正确的节点中。Cluster中存在两条特殊的error指令:

MOVED指令:用来纠正槽位,客户端会刷新自己的槽位关系表。 ASKING指令:用来临时纠正槽位,客户端不会刷新自己的槽位关系表。

9、集群变更感知

当服务器节点变更时,客户端如何得到通知并实时刷新自己的节点关系表?分为两种情况:

目标节点挂掉了,客户端会抛出一个 ConnectionError,紧接着会随机挑选一个节点来重试,这时被重试的节点会通过MOVED指令告知目标槽位被分配到新的节点地址。 运维手动修改集群信息,将主节点切换为其他节点,并将主节点移除。这时打在旧节点上的指令会收到ClusterDown的错误,告知当前节点所在集群不可用(当前节点已被孤立,不在属于之前集群),这时客户端就会关闭所有连接,清空槽位映射关系表,然后向上报错,待下一条指令过来时,就会重新尝试初始化节点信息。

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: