hash取模运算有个比较严重的问题,假设根据当前数据表的量以及增长情况,我们把一个大表拆分成了4个小表,看起来满足目前的需求,但是经过一段时间的运行后,发现四个表不够,需要再增加4个表来存储,这种情况下,就需要对原来的数据进行整体迁移,这个过程非常麻烦。
一般为了减少这种方式带来的数据迁移的影响,我们会采用一致性hash算法。
一致性hash算法在前面我们讲的hash取模算法,实际上对目标表或者目标数据库进行hash取模,一旦目标表或者数据库发生数量上的变化,就会导致所有数据都需要进行迁移,为了减少这种大规模的数据影响,才引入了一致性hash算法。
如图6-7所示,简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-232-1(即哈希值是一个32位无符号整形),什么意思呢?
就是我们通过0-232-1的数字组成一个虚拟的圆环,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到232-1,也就是说0点左侧的第一个点代表232-1。我们把这个由2的32次方个点组成的圆环称为hash环。
图6-7那一致性hash算法和上面的虚拟环有什么关系呢?继续回到前面我们讲解hash取模的例子,假设现在有四个表,table_1、table_2、table_3、table_4,在一致性hash算法中,取模运算不是直接对这四个表来完成,而是对232来实现。
hash(table编号)%232
通过上述公式算出的结果一定是一个0到232-1之间的一个整数,然后在这个数对应的位置标注目标表,如图6-8所示,四个表通过hash取模之后分别落在hash环的某个位置上。
图6-8好了,到目前为止,我们已经把目标表与hash环联系在了一起,那么接下来我们需要把一条数据保存到某个目标表中,怎么做呢?如图6-9所示,当添加一条数据时,同样通过hash和hash环取模运算得到一个目标值,然后根据目标值所在的hash环的位置顺时针查找最近的一个目标表,把数据存储到这个目标表中即可。
图6-9不知道大家是否发现了一致性hash的好处,就是hash运算不是直接面向目标表,而是面向hash环,这样的好处就是当需要删除某张表或者增加表的时候,对于整个数据变化的影响是局部的,而不是全局。举个例子,假设我们发现需要增加一张表table_04,如图6-10所示,增加一个表,并不会对其他四个已经产生了数据的表造成影响,原来已经分片的数据完全不需要做任何改动。
如果需要删除一个节点,同样只会影响删除节点本身的数据,前后表的数据完全不受影响。
图6-10 hash环偏斜上述设计有一个问题,理论情况下我们目标表是能够均衡的分布在整个hash环中,但实际情况有可能是图6-11所示的样子。也就是产生了hash环偏斜的现象,这种现象导致的问题就是大量的数据都会保存到同一个表中,倒是数据分配极度不均匀。
图6-11为了解决这个问题,必须要保证目标节点要均匀的分布在整个hash环中,但是真实的节点就只有4个,如何均匀分布呢?最简单的方法就是,把这四个节点分别复制一份出来分散到这个hash环中,这个复制出来的节点叫虚拟节点,根据实际需要可以虚拟出多个节点出来,如图6-12所示。