一致性哈希算法是分布式系统中常用的算法。 例如,在一个分布式存储系统中,为了将数据存储在特定节点上,使用普通的hash方法将数据映射到特定节点。 例如,key%N,key是数据的key,n是计算机节点数,如果有一台计算机加入或退出此群集,则所有数据映射都将被禁用。 对于持久化存储,执行数据迁移;对于分布式缓存,执行其他缓存
因此,引入了一致性哈希算法:
使用hash函数(例如MD5 )将数据映射到如图所示的大空间。 的数据保存时,首先得到哈希值,对应于环中的每个位置。 例如,k1对应于图中的位置,顺时针找到机器节点b,将k1保存在名为b的节点中。
如果B节点断开,B上的数据将落入C节点,如下图所示。
这样,只影响c节点,而不影响其它节点a、d的数据。 但是,这也会导致“雪崩”,因为c节点承担着b节点的数据,c节点的负荷变大,c节点也容易停机,这样下去整个集群就会挂起。
为此,引入了“虚拟节点”的概念。 也就是说,想象这个环上有很多“虚拟节点”。 数据存储在环中顺时针查找虚拟节点,每个虚拟节点与实际节点相关联。 在下图中使用。
图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器a加载并保存A1、A2的数据,机器b加载并保存B1、B2的数据,机器c加载并保存C1、C2的数据。 由于这些虚拟节点数量多且分布均匀,所以不会发生“雪崩”现象。
Java实现:
publicclassShard{//S类封装了机器节点的信息,如name、password、ip和port
privateTreeMapnodes; //虚拟节点
privateListshards; //实际机器节点
privatefinalintNODE_NUM=100; //每个计算机节点相关的虚拟节点数
公共硬件(list shards ) {
super (;
this.shards=shards;
init (;
}
privatevoidinit ()//初始化一致的哈希
nodes=newTreeMap (;
for(inti=0; I!=shards.size (; I )//每个实际计算机节点必须与一个虚拟节点相关联
finalsshardinfo=Shards.get(I;
for(intn=0; n
//将NODE_NUM个虚拟节点与一个实际的机器节点相关联
nodes.put(hash )、shard-'I'-node-'n )、shardInfo );
}
}
publicsgetshardinfo(stringkey ) {
sortedmaptail=nodes.tail map (hash (key ) ); //顺时针找到虚拟节点
if(tail.size ()==0) {
returnnodes.get(nodes.firstkey );
}
returntail.get(tail.firstkey ); //返回与该虚拟节点对应实际机器节点的信息
}
//*
*MurMurHash算法是一种非加密HASH算法,性能高。
*与传统的CRC32、MD5和SHA-1相比((这两种算法都是加密散列算法,复杂性本身较高,性能下降也是不可避免的) ) ) ) )。
据说等待HASH算法要快得多,而且这个算法的冲突率很低。
* http://murmur hash.Google pages.com /
*/
privatelonghash(stringkey ) {
bytebufferbuf=byte buffer.wrap (key.getbytes () );
intseed=0x1234ABCD;
ByteOrderbyteOrder=buf.order (;
buf.order (byteorder.little _ endian );
longm=0xc6a4a7935bd1e995L;
intr=47;
Longh=seed^(buf.remaining () *m );
长;
while(buf.remaining () )=8) {
k=buf.getLong (;
k*=m;
k^=kr;
k*=m;
h^=k;
h*=m;
}
if(buf.remaining) ) ) )。
bytebufferfinish=byte buffer.allocate (8).order (
ByteOrder.LITTLE_ENDIAN;
//forbig-endianversion,dothisfirst:
//finish.position (8- buf.remaining ) );
finish.put(buf ).rewind );
h^=finish.getLong (;
h*=m;
}
h^=hr;
h*=m;
h^=hr;
BUF.order(byteorder );
返回;
}
}