失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Redis~集群(分布理论 一致性哈希分区 虚拟槽分区 节点握手 集群通信 集群伸缩

Redis~集群(分布理论 一致性哈希分区 虚拟槽分区 节点握手 集群通信 集群伸缩

时间:2023-05-16 08:11:22

相关推荐

Redis~集群(分布理论 一致性哈希分区 虚拟槽分区 节点握手 集群通信 集群伸缩

文章目录

分布理论集群的分布式存储简单哈希一致性哈希分区虚拟槽分区Redis集群功能限制节点握手分配槽集群通信Gossip消息集群伸缩集群扩容集群收缩请求路由MOVED重定向ASK重定向故障转移集群维护集群完整性带宽消耗集群倾斜集群读写分离

分布理论

集群是Redis在3.0版本正式推出的分布式解决方案,有效地解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构方案达到负载均衡的目的并且集群模式有效解决了哨兵模式中哨兵的消耗和哨兵工作的低效率性集群服务是Redis多机运行的最完美的终极方案, 其完全抛弃了主从同步和哨兵模式, 真正实现了多主多从

从上图我们可以看出, 集群拥内部拥有多个主从同步, 因此, 集群有强大的水平扩展能力, 拥有多个主从节点, 理论上性能提高不少, 而且避免了宕机的风险和哨兵的开销

集群的分布式存储

分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集比如我们现在要存储网站的用户登录信息假设有10万个用户在该网站登录, 如果要存储这些数据需要的空间一定是较大的, 单机肯定是不行的, 必须使用分布式存储, 比分布10台机器, 每台机器存储1万个用户并且我们还需要考虑,如何将这10台机器与我们的数据建立起映射关系呢? 换句话来说就是,我们如何确定哪些数据应该放在哪个机器呢?这时就需要用到哈希算法。

简单哈希

我们可以采用除留余数法来完成一个映射,key值为用户账号,余数为机器数量,得到的结果就是应该存储的机器的编号。这样我们将数据放入指定机器中,使用时再根据机器号进入对应的机器进行增删查改即可。机器号 = hash(账号) % 机器数量

但是这个方法存在着一个致命的缺陷,随着用户量不断增多或者用户信息增加,10台机器就会不够用,比如网站日益强大此时有了11万用户,此时就需要将机器扩容至11台。当进行扩容后,由于机器数量发生变化,数据的映射关系也会变化,机器号 = hash(账号) % 机器数量我们就需要进行rehash来将数据重新映射到正确的位置上。但是问题来了,这10台机器的数据如果需要进行重新映射,花费的时间几乎是不可想象的,我们不可能说为了迁移数据而让服务器宕机数月之久,所以这种方法是不可能行得通的。

一致性哈希分区

为了弥补上一种方法的,就引入了一致性哈希算法。上面一种方法的主要缺陷就是由于扩容后rehash带来的数据大量迁移问题。为了解决上述问题,一致性哈希将哈希构造成一个0~2^32-1的环形结构,并将余数从原来的机器数量修改值为整型最大值(也可以是比这个更大的)。因为这个数据足够大,所以不需要考虑因为机器数增加导致的完全rehash问题。机器号 = hash(账号) % 2^32

我们将环中的某一区间去映射到某台服务器,让这台服务器负责这个区间的管理,这样就能让这10台服务器来切分这个闭环结构

当我们要查询某个数据的时候,根据哈希函数算出的映射位置来找到包含该位置的那个区间所对应的服务器,然后在那个服务器中进行操作即可

如果原先的服务器不够用了,此时增加1个服务器,也不需要像之前一样对所有机器的数据进行迁移,我们只需要迁移负载重的机器即可, 将原先这台机器负责的区间进行划分一人再负责一半区间, 然后将原先服务器的数据进行rehash即可

从这里我们可以看到,一致性哈希将服务器数据的整体迁移变成了高负载服务器的部分迁移,大大提高了效率以及稳定性。所以一致性哈希就是一个大范围的闭环,由于除数过大,我们也不需要因为由于除数 数量的增加导致全体rehash。并且映射关系变味了数据区间——机器,如果要增加机器,就只需要改变映射范围,并将区间中的小部分数据进行迁移,大大的提高了效率。这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响但一致性哈希分区存在问题:当使用少量节点时节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案, 此时就会出现这种情况,部分节点数据过少,而部分节点数据过多,此时的数据大量集中在一个节点上,因为节点分布不均匀而导致数据倾斜问题。 会有人想, 那就可以考虑在不增加服务器的基础上多增加几个节点,所以为了解决这问题,一致性哈希又引入了虚拟节点。对每个服务节点进行多次哈希映射,每个映射的位置都会放置该服务节点,成为虚拟节点。例如下图,就分别将NodeA和NodeB分成了三个虚拟节点。我们不需要改变数据定位的算法,只需要将虚拟节点与服务节点进行映射,将定位到虚拟节点NodeX #1、#2、#3的节点再定位回服务节点即可。

因为redis是一个高速的存储服务器 , 即使使用一致性hash在扩容的时候还是会导致一个节点的部分时间不可用 **所以采用虚拟节点要增加或者减少服务器的时候就可以可以将原来的虚拟节点变成一个真的节点, 将原来由这个虚拟节点映射过来的数据直接复制到新节点上, 就省去了rehash的操作

虚拟槽分区

正因为一致性哈希分区的这些缺点,一些分布式系统采用虚拟槽对一致性哈希进行改造其实本质上虚拟槽中的槽就是大量的虚拟节点的抽象化, 将原来的虚拟节点变成一个槽, 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集群功能限制

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 6385redis-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):指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的结果。如果是持有槽的主节点故障,需要为该节点进行故障转移

这个故障转移和哨兵模式的故障转移很像很像

集群维护

Redis集群由于自身的分布式特性,相比单机场景在开发和运维方面存在一些差异

集群完整性

为了保证集群完整性,默认情况下当集群16384个槽任何一个没有指派到节点时整个集群不可用。这是对集群完整性的一种保护措施,保证所有的槽都指派给在线的节点。但是当持有槽的主节点下线时,从故障发现到自动完成转移期间整个集群是不可用状态,对于大多数业务无法容忍这种情况, 因此建议将参数cluster-require-full-coverage配置为no,当主节点故障时只影响它负责槽的相关命令执行,不会影响其他主节点的可用性

带宽消耗

集群内Gossip消息通信本身会消耗带宽,官方建议集群最大规模在1000以内,也是出于对消息通信成本的考虑,因此单集群不适合部署超大规模的节点包括发送的频率, 数据量, 节点数目

集群倾斜

集群倾斜指不同节点之间数据量和请求量出现明显差异,这种情况将加大负载均衡和开发运维的难度。因此需要理解哪些原因会造成集群倾斜,从而避免这一问题数据倾斜主要分为以下几种:节点和槽分配严重不均不同槽对应键数量差异过大集合对象包含大量元素内存相关配置不一致

集群读写分离

集群模式下的读写分离,主节点只负责写数据, 从节点只负责读数据所以同样会遇到:复制延迟,读取过期数据,从节点故障等问题其解决办法依然和主从复制模式下的解决办法相同

/Shangxingya/article/details/111451767

Redis~集群(分布理论 一致性哈希分区 虚拟槽分区 节点握手 集群通信 集群伸缩 请求路由 故障转移 集群维护)

如果觉得《Redis~集群(分布理论 一致性哈希分区 虚拟槽分区 节点握手 集群通信 集群伸缩 》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。