Redis分布式缓存方案
  wMDCgnRK3Ovr 2023年11月28日 42 0

1.分布式存储

1.1 问题

1~2亿条数据需要缓存,如何设计存储案例。

1.2 解决

单机单台100%不可能,肯定是分布式存储,用redis如何落地?

并且在分布式缓存服务中,经常需要对服务进行节点添加和删除操作,我们希望的是节点添加和删除操作尽量减少数据-节点之间的映射关系更新(节点数的增加与减少对数据命中节点影响不大)

1.2.1 哈希取余分区

2亿条记录就是2亿个k,v,单机不行必须要分布式多机,假设有3台机器构成一个集群,用户每次读写操作都是根据公式:hash(key) % N个机器数,计算出哈希值,用来决定数据映射到哪一个节点上。

Redis分布式缓存方案_一致性哈希

优点

简单粗暴,直接有效,只需要预估好数据规划好节点,例如3台,8台,10台,就能保证一段时间的数据支撑。使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。

缺点

原来规划好的节点,进行扩容或者缩容就比较麻烦了,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:hash(key) % 3会变成hash(key) % ?。此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。

某个redis机器宕机了,由于台数数量的变化,会导致hash取余全部数据重新洗牌。

1.2.2 一致性哈希算法分区

一致性哈希算法在1997年由麻省理工学院提出,设计目标是解决分布式缓存数据变动和映射问题,某个机器宕机了,分母数量改变了,自然取余数就不OK了。

步骤一:构建一致性哈希环

一致性哈希算法必然有个hash函数并按照算法产生hash值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个hash空间[0, 232-1],这是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它收尾相连(0 = 232),这样让它逻辑上形成了一个环形空间。

它也是使用取模的方法,哈希取余分区法是对节点(服务器数据进行取模)。而一致性哈希算法是对232取模。简单来说,一致性哈希算法将整个哈希值空间组织成一个虚拟的圆环。假设某哈希函数H的值空间为0 - 232-1(即哈希值是一个32位无符号整形),整个哈希环如下图:整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,,以此类推,2,3,4,……知道232-1,也就是说0点左侧的第一个点代表232-1,0和232-1在零点中方向重合,我们把这个由232个点组成的圆环成为哈希环。

Redis分布式缓存方案_redis分布式缓存_02

步骤二:服务器IP节点映射

节点映射:将集群中各个IP节点映射到环上的某一个位置。

将各个服务器使用Hash函数进行哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如4个节点NodeA,B,C,D使用IP地址的哈希函数计算(hash(ip))后在环空间的位置如下:

Redis分布式缓存方案_redis分布式缓存_03

步骤三:key落到服务器上的落键规则

当我们需要存储一个kv键值对时,首先计算key的hash值,hash(key),确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。

如果我们有ObjectA,ObjectB,ObjectC,ObjectD四个数据对象,经过哈希计算后,在环空间上的位置如下。根据一致性哈希算法,数据A会被定位到Node A上,B被定义到Node B上,C被定义到Node C上,D被定义到Node D上。

Redis分布式缓存方案_redis分布式缓存_04

优点

优点一:一致性哈希算法的容错性

假设Node C宕机,可以看到此时A,B,D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(沿着逆时针方向行走遇到的第一台服务器)之间的数据,其他不会受到影响。简单来说,就是C宕机,受到影响的只是B,C之间的数据,并且这些数据会转移到D存储。

优点二:一致性哈希算法的扩展性

数据量增加了,需要增加一台节点NodeX,NodeX的位置在A和B之间,那么受到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致哈希取余全部数据重新洗牌。

Redis分布式缓存方案_一致性哈希_05

缺点

缺点一:一致性哈希算法的数据倾斜问题

一致性哈希算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题,例如系统中只有两台服务器时:

Redis分布式缓存方案_一致性哈希_06

缺点二:一致性哈希算法的雪崩问题

删除节点NodeB后,原本在NodeB节点上的数据就会被重新定位到NodeC上。这样就产生一个影响:原来NodeB的数据转移到NodeC上,这样NodeC的内存使用率会骤增。如果NodeB上存在热点数据,NodeC会扛不住甚至挂掉。挂掉之后数据又转移给NodeD。如此循环会导致所有节点崩溃,也就是所说的雪崩的情况。

虚拟节点

为了解决数据倾斜现象和雪崩现象,提出了虚拟节点这个概念。就是将真实节点计算多个哈希形成多个虚拟节点并放置到哈希环上,定位算法不变,只是多了一步虚拟节点到真实节点映射的过程。

以雪崩现象来说明,如下图节点real1节点有两个虚拟节点v100和v101,real2有两个虚拟节点v200和v201,real3节点有v300和v301两个虚拟节点:

Redis分布式缓存方案_redis分布式缓存_07

当real1节点挂掉后,v100和v101节点也会随即消失,这是k1数据就会被分配到v301上,k4就会被分配到v200上,这就解决了雪崩的问题。当某个节点宕机后,其数据并没有全部分配给某一个节点,而是被分到了多个节点上(理想情况下)。

注意:

  • 真实节点不放置到哈希环上,只有虚拟节点才会放上去
  • 为什么要使用闭合的哈希环?举个例子,如果在223-3处有一个key,而223-3 ~ 223处并没有节点,那么这个key该存在哪个节点呢?
  • 一致性哈希使用的32个bits,4个bytes,肯定也会有哈希碰撞的问题存在
1.2.3 哈希槽分区

哈希槽实质就是一个数组,数组[0, 214-1]形成hash slot空间。

解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系。现在就相当于节点上放的是槽,槽里面放的是数据。

Redis分布式缓存方案_redis分布式缓存_08

槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。

哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配。

一个集群只能有16384个槽,编号0 - 16383(0 - 214-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。

哈希槽计算

Redis集群中内置了16384个哈希槽,redis会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在Redis集群中放置一个key-value时,redis先对key使用CRC16算法算出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0 - 16383之间的哈希槽,也就是映射到某个节点上。如下代码,keyA,B落在Node2,keyC落在Node3上。

Redis分布式缓存方案_一致性哈希_09

Redis分布式缓存方案_redis分布式缓存_10

面试题

一致性hash算法是216,为什么hash slot是214

在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384 = 16K。在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 * 1024 = 16k),也就是说使用2k的空间创建了16k的槽数。虽然使用CRC16算法最多可以分配65535(216-1)个槽位,65535 = 65k,压缩后就是8k(8 * 8 * 1024 = 65k),也就是说需要8k的心跳包。作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。


注:本篇文章参考自多个博文。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月28日 0

暂无评论

wMDCgnRK3Ovr