一致性哈希算法的研究
之前由于一直在做客户端开发,对分布式内存缓冲数据存储的原理一直都存有很多疑问,想着无非是哈希算法,或者分级的数据查询,但这两种方式都有很多缺点,使用哈希算法扩展起来很麻烦,分级的话会造成最上级节点瘫痪,导致所有数据请求无法查询。
最近的新工作需要做一些大数据处理,也了解了一些memcache,HazelCast之类一些缓存系统,从网上搜了一些实现原理的资料,发现其核心是一种叫做一致性哈希的算法,不过我觉得叫“连续hash算法”更合适点,这篇文章上有些详细的介绍 http://blog.csdn.net/sparkliang/article/details/5279393。
C语言版本的算法http://www.codeproject.com/Articles/56138/Consistent-hashing
Java版本的算法 https://weblogs.java.net/blog/2007/11/27/consistent-hashing
其实核心算法代码很少,很简单,下面对Java的一致性哈希算法简单做个注释
import java.util.Collection; import java.util.SortedMap; import java.util.TreeMap; public class ConsistentHash<T> { //HashFunction普通哈希算法函数 private final HashFunction hashFunction;
//numberOfReplicas虚拟节点数量 private final int numberOfReplicas;
//环形数值空间,key为server address的hash,value也可以说是服务器地址 private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>(); //构造,哈希算法函数,壹个真实节点的虚拟节点个数,真实节点集合 public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) { this.hashFunction = hashFunction; this.numberOfReplicas = numberOfReplicas; for (T node : nodes) { add(node); } } //添加新的真实节点 public void add(T node) { for (int i = 0; i < numberOfReplicas; i++) {
//产生虚拟节点,并均匀分布出去,虚拟几点是真实节点的地址加上壹个连续数字,并且虚拟节点的value都指向真实节点 circle.put(hashFunction.hash(node.toString() + i), node); } } //移除真实节点,并将节点关联的虚拟节点一并删除 public void remove(T node) { for (int i = 0; i < numberOfReplicas; i++) { circle.remove(hashFunction.hash(node.toString() + i)); } } //通过Object key获取其数据存储的服务器 public T get(Object key) { if (circle.isEmpty()) { return null; }
//获取object key的hash int hash = hashFunction.hash(key);
//节点存在则直接返回hash指向的服务器节点(几率为0.0000...1%),否则则按照一致性哈希算法查找临近服务器节点 if (!circle.containsKey(hash)) {
//返回小于当前哈希的有序哈希空间 SortedMap<Integer, T> tailMap = circle.tailMap(hash);
//如果有序哈希空间为空,则返回circle map的第一个key对应的值(因为是个环形),否则则返回有序哈希空间的第一个key对应值 hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey(); } return circle.get(hash); } }
注:circle中只存储虚节点地址的key-value,不存储object key,这个是刚开始的时候误会了的,我想其他很多人也和我一样被那个算法图误解了,因此前端算法执行的服务器是也是可以横向扩展的。