分布式系统中Hash算法运用

1、分布式系统和集群的区别

1.1、分布式系统:指的是将一个大的应用按照功能职责或者业务的不同划分为不同的模块,各个模块可以独立部署,通过http/https(更安全)或者其它的方式进行交互。

1.2、集群:指的是将多个相同的应用部署在多个服务器上,同时对外提供服务。

2、节点取余:假如你的缓存服务器有10台,在缓存数据时,你会取业务字段写一个hash算法,经过hash算法后会有一个hash值,拿hash值对10取模,得到的是0-9的数字,再将数字和缓存服务器映射,得到的就是你的数据具体存储到那台缓存服务器。
2.1、问题:这样的设计拓展和容错都不好,假如你发现现在的10台服务器不能满足性能的要求,你需要增加服务器个数,譬如服务器由10->11;原先hash值为11的数据被缓存在1这台服务器,服务器变为11后,这个数据被缓存到0这台服务器上,那么再根据这个hash值去取数据时同样定位到0这台服务器,发现很多缓存数据没有,大量的缓存失效了,导致请求打到了数据库,接而导致数据库压力,也就是说的"缓存雪崩"。

拓展:其实你会发现这样的设计在开发中会时常遇到,举例,之前在某行担任后端研发,负责后端的一个项目,我们当时的数据的量还是比较大的,然后我们根据业务的字段,写了个hash算法,得到一个hash值拿这个hash值对10取模,也就是所谓的水平分表,我们将数据库的一张表按照业务分成了10张表,的确在短时间内,我们发现整个系统在这块的业务的查询和其他的操作明显快了很多,但是这里会有一个问题,就是当表的数据量提升上去后,你会发现原先10张表已经满足不了性能的要求,这怎么处理:我们当时的做法,我们只保留一个用户最近半年的数据,其余的数据我们保存在备份表里,个人觉得这样的处理方式还是可行且有效的;原因假如一张表的操作只有insert,或者你的insert远大于delete,那么必然会存在某一天你的操作会因为表的数据量变多而变得效率低下,此时必须要建立一个符合业务的数据库定时清理规则。

2.2、针对2.1有没有好的解决方案:

一致性hash算法,会通过hash1()算法将整个hash值设计成一个闭合的环状,同时会将服务器节点通过hash2()函数映射到环上,当有一个key过来时,用hash1()函数也映射到环上,接着顺时针对该key进行存储或者命中读取,过程设计如下图

2.2.1、 hash机器节点

      首先求出机器节点的hash值(怎么算机器节点的hash?i可选用ip可以作为hash的参数吧,当然还有其他的方法了),然后将其分布到0~2^32的一个圆环上(顺时针分布)。如下图所示:

分布式系统中Hash算法运用_第1张图片

 集群中有机器:A , B, C, D, E五台机器,通过一定的hash算法我们将其分布到如上图所示的环上。

2.2.2、访问方式

      如果有一个写入缓存的请求,其中Key值为K,计算器hash值Hash(K), Hash(K) 对应于上图环中的某一个点,如果该点对应没有映射到具体的某一个机器节点,那么顺时针查找,直到第一次找到有映射机器的节点,该节点就是确定的目标节点,如果超过了2^32仍然找不到节点,则命中第一个机器节点。比如 Hash(K) 的值介于A~B之间,那么命中的机器节点应该是B节点(如上图 )。

2.2.3、增加节点的处理

      在原有集群的基础上欲增加一台机器F,增加过程如下:

计算机器节点的Hash值,将机器映射到环中的一个节点,如下图:

分布式系统中Hash算法运用_第2张图片

       增加机器节点F之后,访问策略不改变,依然按照2.2.2中的方式访问,此时缓存命不中的情况依然不可避免,不能命中的数据是hash(K)在增加节点以前落在C~F之间的数据。尽管依然存在节点增加带来的命中问题,但是比较传统的 hash取模的方式,一致性hash已经将不命中的数据降到了最低。

       这也是为什么redis并没有使用一致性哈希算法而是使用虚拟槽这种替代方案,因为虚拟槽技术使得每一个节点管理的槽点是固定的,即使你增加了节点也是需要其他节点将槽点分配给新节点,新节点才有资格管理槽,因此就不会出现数据丢失的问题。

你可能感兴趣的:(算法,哈希算法,java,分布式缓存)