随着memcache、redis以及其它一些内存K/V数据库的流行,一致性哈希也越来越被开发者所了解。因为这些内存K/V数据库大多不提供分布式支持(本文以redis为例),所以如果要提供多台redis server来提供服务的话,就需要解决如何将数据分散到redis server,并且在增减redis server时如何最大化的不令数据重新分布,这将是本文讨论的范畴。
取模算法
取模运算通常用于得到某个半开区间内的值:m % n = v,其中n不为0,值v的半开区间为:[0, n)。取模运算的算法很简单:有正整数k,并令k使得k和n的乘积最大但不超过m,则v的值为:m - kn。比如1 % 5,令k = 0,则k * 5的乘积最大并不超过1,故结果v = 1 - 0 * 5 = 1。
我们在分表时也会用到取模运算。如一个表要划分三个表,则可对3进行取模,因为结果总是在[0, 3)之内,也就是取值为:0、1、2。
在redis场景中,使用取模算法,如果机器数量变化,则会造成缓存key的重新落点到新的机器,这样会造成很多缓存失效,从而对服务器造成压力,高并发的情况下,还可能造成雪崩。 而一致性hash算法可以很好的缩小影响范围,下面简单介绍下hash算法。
哈希(Hash)
Hash,一般翻译做“散列”,也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
目前普遍采用的哈希算法是time33,又称DJBX33A (Daniel J. Bernstein, Times 33 with Addition)。这个算法被广泛运用于多个软件项目,Apache、Perl和Berkeley DB等。对于字符串而言这是目前所知道的最好的哈希算法,原因在于该算法的速度非常快,而且分类非常好(冲突小,分布均匀)。
PHP内核就采用了time33算法来实现HashTable,来看下time33的定义:
hash(i) = hash(i-1) * 33 + str[i]
有了定义就容易实现了:
<?php
function myHash($str) {