一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。该算法是1997年在论文《Consistent hashing and random trees》中被提出,在分布式系统中应用非常广泛。一致性哈希是一种哈希算法,简单地说在移除或者添加一个服务器时,此算法能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系,尽可能满足单调性的要求。本文将从分布式数据库的数据分发的角度简单介绍下一致性Hash的原理。
背景
分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即数据的sharding问题。最常规的方式就是hash进行分区(节点取余法)。这种方法可以做数据分布,但是这样的系统scalability很差,我们想要加入一个节点或者删除一个节点都会有问题。
比如将数据分布在A,B,C三个节点:
当加入新的节点D的时候,我们需要对4取余了,那么之前存入的全部数据就需要进行重新rehash迁移。否则,我们之前存入的数据通过%4将无法映射到准确位置,就无法获取到数据(如果是Redis的缓存分布的话,请求就会打到DB,造成缓存雪崩)。
原理
一致性hash
先想象一下有一个由2^32个结点组成的一个环:
这个环我们叫做hash环。这时候我们就需要将有的服务器节点hash到这个环上。即 {A,B,C}%2^32:
这是对服务器节点先hash到节点上。然后我们这时候就要让数据分布在这些节点上。这时候比如数据{a,b,c,d...}需要存入节点了,同样的我们将这些数据hash到这个hash环上。这里采取的策略是:将数据按照顺时针方向,找到第一个大于等于该哈希值的结点。
所以a将保存到A上,b将保存到B上,以此类推。
这么做有什么好处呢?
这时候我们加入新的节点D:
可以看到,除了上面加入D之后红线那部分数据是将无法准确映射的(数据还在A上)。其他的大部分数据还是可以使用的。**所以我们只需要将红色那小部分数据重新迁移就行了。**这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。
还存在什么缺点?
加入我们的hash后的节点分布不均匀,就会导致倾斜问题。一旦倾斜了,那么还是会导致很大一部分数据在加入节点后可能都会失效。并且还会导致数据的分布很不均匀(如下图,概率上大部分数据都会存入A节点)。
解决方案
添加虚拟节点
虚拟槽分区
虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。这个范围一般远远大于节点数,比如Redis Cluster槽范围是16384个(0~16383)。槽是集群内数据管理和迁移的基本单位。
其基本思想就是对现有的节点,给其映射若干个虚拟节点,然后将这些虚拟节点再映射到hash环上。比如A,B,C虚拟出A1~A5,B1~B10,C1~C10。
这样相当于一个节点,管理了hash环上多个槽。这样可以很大概率的保证分布的均匀性。
总结
一致性hash是用来处理分布式场景下,数据分布在系统扩展的时候,如何减少cost。相对来说,原理还是比较简单的。