• 一致性哈希算法


    一致性哈希算法

    一致性哈希算法(Consistent Hasing,以下简称CH)是一种特殊的哈希算法,使用CH的哈希表扩容的时候,平均只有K/n个关键字需要被重新映射(或者移动),这里的K是关键字的数量,n指的是哈希表的槽位(slot)。

    而传统的哈希表在扩容的时候,几乎所有的关键字需要被重新映射(或移动)。

    传统的哈希表的工作流程如下:
    设一个哈希表有nslot,每个关键字key通过哈希函数得到一个哈希值h,然后通过取模h%n得到所在的slot位置。

    当哈希表扩容的时候,上述的n发生了变化,因此原来的关键字和slot之间的映射关系发生了变化,现在需要重新建立这种关系。当这个哈希表特别大的时候,涉及到的关键字特别多,这种扩容带来的代价十分巨大。

    基于传统哈希表的这个局限,一致性哈希算法通过将映射后的空间组织成一个哈希环Circle,每个环上有多个结点Node,因此结点将映射后的空间划分成了多段,当新增或者删除结点的时候,只需要重新映射某一段的数据即可。

    CH常常作为一种负载均衡算法,在很多地方都得到了应用。

    关键字的定位

    示意图如下:

    上述示意图描述了一个具有8个节点的哈希环,当一个关键字需要定位到某个节点的时候,首先根据哈希函数计算关键字的哈希值h,然后这个哈希值必定落在某个区间里面,顺时针找到第一个大于该哈希值的节点,将关键字存储到该结点。

    节点删除

    当由于主机宕机、负载过高等原因,需要暂时将某个节点从环中删除,此时CH不需要改变所有的数据映射的关系,而只是修改部分数据的映射关系即可。

    示意图如下:

    这里写图片描述

    当节点sn1被删除的时候,哈希值落在sn0到sn1之间的关键字在定位的时候会定位到sn2,因此需要将sn1的数据拷贝到sn2节点上。

    节点增加

    同节点删除类似,示意图如下:

    当在sn1和sn2之间增加了节点sn8,需要将sn2的数据迁移到sn8节点上。

    虚拟节点

    当哈希环上的节点的数量比较少的时候,可能会出现节点在环上分布不均匀,例如出现以下这种情况:

    这种情况必然会造成大量的请求转发给服务器1,造成服务器1不堪重负,而服务器2却无所事事,这就是所谓的负载不均衡。
    因此在环上节点分布不均匀的时候,可以增加若干个虚拟节点来有效的解决这个负载不均衡的问题。

    于是可以增加“Server 1#1”、“Server 1#2”、“Server 1#3”、“Server 2#1”、“Server 2#2”、“Server 2#3”,于是形成六个虚拟节点,这样在真实节点少的情况下,可以将空间划分得更加均匀一点。

    代码实现

    
    public class ConsistentHash<T> {
        interface HashFunction {
            int hash(String s);
        }
    
        private int numerOfVirtualNode;
    
        private SortedMap<Integer, T> hashCircle = new TreeMap<>();
        private HashFunction hashFunction;
    
        public ConsistentHash (int numerOfVirtualNode, Collection<T> nodes) {
            this(null, numerOfVirtualNode, nodes);
        }
    
    
        public ConsistentHash (HashFunction hf, int numerOfVirtualNode, Collection<T> nodes) {
            this.numerOfVirtualNode = numerOfVirtualNode;
            this.hashFunction = hf;
            for (T node : nodes) {
                add(node);
            }
        }
    
        /**
         * 增加一个节点
         * @param node
         */
        public void add(T node) {
            // TODO Auto-generated method stub
            for (int i = 0; i < numerOfVirtualNode; i++) {
                hashCircle.put(hashFunction.hash(node.toString() + i), node);
            }
            //这里省略了增加节点以后,数据迁移的操作。
        }
    
        /**
         * 删除一个节点
         * @param node
         */
        public void remove(T node) {
            for (int i = 0; i < numerOfVirtualNode; i++) {
                hashCircle.remove(hashFunction.hash(node.toString() + i));
            }
            //这里省略了删除节点以后,数据迁移的操作。
        }
    
        /**
         * 顺时针寻找第一个大于key的hash值的节点
         * @param key
         * @return
         */
        public T get(Object key) {
            if (hashCircle.isEmpty()) {
                return null;
            }
            int hash = hashFunction.hash(key.toString());
            if (!hashCircle.containsKey(hash)) {
                SortedMap<Integer, T> tailMap = hashCircle.tailMap(hash);
                hash = tailMap.isEmpty() ? hashCircle.firstKey() : tailMap.firstKey();
            }
            return hashCircle.get(hash);
        }
    
        //default hash function
        static class MD5HashFunction implements HashFunction {
            MessageDigest md5 = null;
            public MD5HashFunction() {
                // TODO Auto-generated constructor stub
                try {
                    md5 = MessageDigest.getInstance("MD5");
                } catch (NoSuchAlgorithmException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
            @Override
            public int hash(String s) {
                // TODO Auto-generated method stub
                md5.reset();
                byte[] res = md5.digest(s.getBytes());
                int hash = ((res[0] & 0xFF) << 24) | ((res[1] & 0xFF) << 16)| ((res[2] & 0xFF) << 8) | ((res[3] & 0xFF)); 
                return hash;
            }
        }
    }

    references

    1. http://blog.51cto.com/alanwu/1431397
    2. https://en.wikipedia.org/wiki/Consistent_hashing
    3. http://www.cnblogs.com/moonandstar08/p/5405991.html
    4. https://web.archive.org/web/20120605030524/http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html
  • 相关阅读:
    转Vtype扩展
    Can't connect to MySQL server on 'ip' (13)
    观察者+js 模式
    (转)ASP.NET架构分析
    sql得到时间
    Js+XML 操作 (转)
    js中的math对象
    property和attribute的区别
    CSS样式定义
    linux 开启 mount
  • 原文地址:https://www.cnblogs.com/Spground/p/9567878.html
Copyright © 2020-2023  润新知