-
正因为一致性哈希分区的这些缺点,一些分布式系统采用虚拟槽对一致性哈希进行改造
-
其实本质上虚拟槽中的槽就是大量的虚拟节点的抽象化, 将原来的虚拟节点变成一个槽, redis内置是有16383个槽也就是有16383个虚拟节点
-
虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围 一般远远大于节点数,比如Redis Cluster槽范围是0~16383
-
如下图所示:当前集群有5个节点,每个节点平均大约负责3276个槽
-
由于采用高质量的哈希算法,每个槽所映射的数据通常比较均匀,将数据平均划分到5个节点进行数据分区, 所以redis使用的就是虚拟槽分区
-
采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内,计算公式:slot=CRC16(key)&16383。每一个节点负责维护一部分槽以及槽所映射的键值数据,如下图所示
- Redis虚拟槽分区的特点:
-
解耦数据和节点之间直接关系, 一致性hash计算方式是key & 节点个数, 映射的结果和节点个数有关系, 但是使用hash槽计算方式是 CRC16(key) % 槽的个数, 所以就解耦了数据和节点的关系
-
使用哈希槽的好处就在于可以方便的添加或移除节点。
-
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
-
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;
-
在这一点上,我们以后新增或移除节点的时候不用先停掉所有的 redis 服务甚至不用停到任何一个节点的服务。对应槽的采用直接复制数据过去显然比rehash快很多
- 节点自身维护槽的映射关系, 不需要客户端和代理服务器进行维护处理
- Redis集群相对单机在功能上存在一些限制
-
key批量操作支持有限。如mset(批量赋值)、mget(批量取值),目前只支持具有相同slot值的 key执行批量操作。对于映射为不同slot值的key由于执行mget、mget等操作可能存在于多个节点上因此不被支持
-
key事务操作支持有限。同理只支持多key在同一节点上的事务操作,当多个key分布在不同的节点上时无法使用事务功能
-
key作为数据分区的最小粒度,因此不能将一个大的键值对象如 hash、list等映射到不同的节点
-
Redis集群一般由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。
-
当我们启动6个节点,但每个节点彼此并不知道对方的存在,通过节点握手让6个节点彼此建立联系从而组成一个集群
-
节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信, 达到感知对方的过程
-
节点握手是集群彼此通信的第一步,由客户端发起下面的命令
cluster meet {ip} {port}
- 上面执行命令之后让节点6379和6380节点进行握手通信。cluster meet命令是一个异步命令,执行之后立刻返回。内部发起与目标节点进行握手通信,如下图所示:
1)节点6379本地创建6380节点信息对象,并发送meet消息
2)节点6380接受到meet消息后,保存6379节点信息并回复pong消息
3)之后节点6379和6380彼此定期通过ping/pong消息进行正常的节点通信
- 这里的meet、ping、pong消息是Gossip协议通信的载体,之后的节点通信部分做进一步介绍,它的主要作用是节点彼此交换状态数据信息。6379和6380节点通过meet命令彼此建立通信之后,集群结构如下图所示:
- 下面分别执行meet命令让其他节点加入到集群中,我们只需要在集群内任意节点上执行cluster meet命令加入新节点,握手状态会通过消息在集群内通过ping pong传播,这样其他节点会自动发现新节点并发起握手流程:
分配槽
- Redis集群把所有的数据映射到16384个槽中。每个key会映射为一个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。通过
cluster addslots
命令为节点分配槽。这里利用bash特性批量设置槽(slots)
redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0…5461}
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462…10922}
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923…16383}
-
作为一个完整的集群,每个负责处理槽的节点应该具有从节点,保证当它出现故障时可以自动进行故障转移。集群模式下,Reids节点角色分为主节点和从节点。首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。
-
Redis集群模式下的主从复制使用了之前介绍的Redis复制流程,依然支持全量和部分复制和增量复制
-
在分布式存储中需要提供维护节点元数据信息的机制,所谓元数据是指:节点负责哪些数据,是否出现故障等状态信息
-
Redis集群采用P2P的Gossip(流言)协议, Gossip协议工作原理就是节点彼此不断通信交换信息,一段时间后所有的节 点都会知道集群完整的信息,这种方式类似流言传播,如下图所示:
通信过程说明:
1)集群中的每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信
2)每个节点在固定周期内通过特定规则选择几个节点发送ping消息
3)接收到ping消息的节点用pong消息作为响应
-
集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,只要这些节点彼此可以正常通信,最终它们会达到一致的状态。
-
当节点出故障、新节点加入、主从角色变化、槽信息 变更等事件发生时,通过不断的ping/pong消息通信,经过一段时间后所有的 节点都会知道整个集群全部节点的最新状态,从而达到集群状态同步的目的
Gossip消息
-
Gossip协议的主要职责就是信息交换。为了实现信息大一统
-
常用的Gossip消息可分为:ping消息、pong消息、meet消息、fail消息等
-
meet消息:用于通知新节点加入。消息发送者通知接收者加到当前集群,meet消息通信正常完成后,接收节点会加入到集群中并进行周期性的 ping、pong消息交换
-
ping消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其 他节点发送ping消息,用于检测节点是否在线和交换彼此状态信息。ping消息发送封装了自身节点和部分其他节点的状态数据。
-
pong消息:当接收到ping、meet消息时,作为响应消息回复给发送方确认消息正常通信。pong消息内部封装了自身状态数据。节点也可以向集群内 广播自身的pong消息来通知整个集群对自身状态进行更新
-
fail消息:当节点判定集群内另一个节点下线时,会向集群内广播一个 fail消息,其他节点接收到fail消息之后把对应节点更新为下线状态。
- Redis集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容,如下图所示:
集群扩容
- 扩容是分布式存储最常见的需求,Redis集群扩容操作可分为如下步骤:
- 准备新节点
启动后的新节点作为孤儿节点运行,并没有其他节点与之通信
- 加入集群
新节点依然采用cluster meet命令加入到现有集群中。在集群内任意节点执行cluster meet命令让6385和6386节点加入进来
redis-cli -p 6379 cluster meet 127.0.0.1 6385
redis-cli -p 6379 cluster meet 127.0.0.1 6386
- 集群内新旧节点经过一段时间的ping/pong消息通信之后,所有节点会发现新节点并将它们的状态保存到本地
- 迁移槽和数据
- 槽是Redis集群管理数据的基本单位,首先需要为新节点制定槽的迁移计划,确定原有节点的哪些槽需要迁移到新节点。迁移计划需要确保每个节点负责相似数量的槽,从而保证各节点的数据均匀
- 数据迁移过程是逐个槽进行的
集群收缩
- 流程说明:
1)首先需要确定下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性
2)当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记该节点后可以正常关闭
MOVED重定向
- 在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,如果被处理的数据在节点自身,则处理键命令;否则说明数据对应的槽已经迁移完成了回复MOVED重定向错误,通知客户端请求正确的节点
ASK重定向
-
Redis集群支持在线迁移槽(slot)和数据来完成水平伸缩,当slot对应的数据从源节点到目标节点迁移过程中,客户端需要做到智能识别,保证键命令可正常执行。
-
例如当一个slot数据从源节点迁移到目标节点时,期间可能出现一部分数据在源节点,而另一部分在目标节点,如下图所示
- 当出现上述情况时,客户端键命令执行流程将发生变化:
-
客户端根据本地slots缓存发送命令到源节点,如果存在键对象则直接执行并返回结果给客户端
-
如果键对象不存在,则可能存在于目标节点,这时源节点会回复 ASK重定向异常。
-
客户端从ASK重定向异常提取出目标节点信息,发送asking命令到目标节点打开客户端连接标识,再执行键命令。如果存在则执行,不存在则返回不存在信息
ASK与MOVED虽然都是对客户端的重定向控制,但是有着本质区别:
-
ASK重定向说明集群正在进行slot数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新slots缓存
-
但是MOVED重定向说明键对应的槽已经明确指定到新的节点,因此需要更新slots缓存
-
当集群内某个节点出现问题时,需要通过一种健壮的方式保证识别出节点是否发生了故障。Redis集群内节点通过ping/pong消息实现节点通信,消息不但可以传播节点槽信息,还可以传播其他状态如:主从状态、节点故障等
-
主观下线 (pfail):指某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障判定,只能代表一个节点的意见,可能存在误判情况
-
客观下线(fail):指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的结果。如果是持有槽的主节点故障,需要为该节点进行故障转移
这个故障转移和哨兵模式的故障转移很像很像
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
加入社区:https://bbs.youkuaiyun.com/forums/4304bb5a486d4c3ab8389e65ecb71ac0
障,需要为该节点进行故障转移
这个故障转移和哨兵模式的故障转移很像很像
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
[外链图片转存中…(img-SUoFA7Wo-1725711857178)]
加入社区:https://bbs.youkuaiyun.com/forums/4304bb5a486d4c3ab8389e65ecb71ac0