# Ketama一致性Hash算法(含Java代码)

3 篇文章 0 订阅

［下面以Memcached的分布式问题为讨论点，但将Memcached server抽象为节点(Node)］

将节点hash后会不均匀地分布在环上，这样大量key在寻找节点时，会存在key命中各个节点的概率差别较大，无法实现有效的负载均衡。
如有三个节点Node1,Node2,Node3，分布在环上时三个节点挨的很近，落在环上的key寻找节点时，大量key顺时针总是分配给Node2，而其它两个节点被找到的概率都会很小。

改善Hash算法，均匀分配各节点到环上；［引文］使用虚拟节点的思想，为每个物理节点（服务器）在圆上分配100～200个点。这样就能抑制分布不均匀，最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上，就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。

Ketama is an implementation of a consistent hashing algorithm, meaning you can add or remove servers from the memcached pool without causing a complete remap of all keys.
Here’s how it works:
* Take your list of servers (eg: 1.2.3.4:11211, 5.6.7.8:11211, 9.8.7.6:11211)
* Hash each server string to several (100-200) unsigned ints
* Conceptually, these numbers are placed on a circle called the continuum. (imagine a clock face that goes from 0 to 2^32)
* Each number links to the server it was hashed from, so servers appear at several points on the continuum, by each of the numbers they hashed to.
* To map a key->server, hash your key to a single unsigned int, and find the next biggest number on the continuum. The server linked to that number is the correct server for that key.
* If you hash your key to a value near 2^32 and there are no points on the continuum greater than your hash, return the first server in the continuum.
If you then add or remove a server from the list, only a small proportion of keys end up mapping to different servers.

Java代码
1.
2. //对所有节点，生成nCopies个虚拟结点
3.         for(Node node : nodes) {
4.             //每四个虚拟结点为一组，为什么这样？下面会说到
5.             for(int i=0; i<nCopies / 4; i++) {
6.                 //getKeyForNode方法为这组虚拟结点得到惟一名称
7.                 byte[] digest=HashAlgorithm.computeMd5(getKeyForNode(node, i));
8.             /** Md5是一个16字节长度的数组，将16字节的数组每四个字节一组，
9.                         分别对应一个虚拟结点，这就是为什么上面把虚拟结点四个划分一组的原因*/
10.                 for(int h=0;h<4;h++) {
11.                   //对于每四个字节，组成一个long值数值，做为这个虚拟节点的在环中的惟一key
12.                     Long k = ((long)(digest[3+h*4]&0xFF) << 24)
13.                         | ((long)(digest[2+h*4]&0xFF) << 16)
14.                         | ((long)(digest[1+h*4]&0xFF) << 8)
15.                         | (digest[h*4]&0xFF);
16.
17.                     allNodes.put(k, node);
18.                 }
19.             }
20.         }

Java代码
1.         final Node rv;
2.         byte[] digest = hashAlg.computeMd5(keyValue);
3.         Long key = hashAlg.hash(digest, 0);
4.         //如果找到这个节点，直接取节点，返回
5.         if(!ketamaNodes.containsKey(key)) {
6.         //得到大于当前key的那个子Map，然后从中取出第一个key，就是大于且离它最近的那个key
7.             SortedMap<Long, Node> tailMap=ketamaNodes.tailMap(key);
8.             if(tailMap.isEmpty()) {
9.                 key=ketamaNodes.firstKey();
10.             } else {
11.                 key=tailMap.firstKey();
12.             }
13.             //在JDK1.6中，ceilingKey方法可以返回大于且离它最近的那个key
14.             //For JDK1.6 version
15. //          key = ketamaNodes.ceilingKey(key);
16. //          if (key == null) {
17. //              key = ketamaNodes.firstKey();
18. //          }
19.         }
20.
21.
22.         rv=allNodes.get(key);

Java代码
1. Nodes count : 5, Keys count : 100000, Normal percent : 20.0%
2. -------------------- boundary  ----------------------
3. Node name :node1 - Times : 20821 - Percent : 20.821001%
4. Node name :node3 - Times : 19018 - Percent : 19.018%
5. Node name :node5 - Times : 19726 - Percent : 19.726%
6. Node name :node2 - Times : 19919 - Percent : 19.919%
7. Node name :node4 - Times : 20516 - Percent : 20.516%

Java代码
1. Normal case : nodes count : 50
2. Added case : nodes count : 51
3. Reduced case : nodes count : 49
4. ------------ boundary -------------
5. Same percent in added case : 93.765%
6. Same percent in reduced case : 93.845%

http://www.iteye.com/topic/684087

• 0
点赞
• 0
收藏
觉得还不错? 一键收藏
• 0
评论
03-12 161
03-14 933
07-18 2710

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。