一致性哈希算法怎么保证数据的一致性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一致性哈希算法怎么保证数据的一致性相关的知识,希望对你有一定的参考价值。

环割法(一致性 hash)环割法的原理如下:

1. 初始化的时候生成分片数量 X × 环割数量 N 的固定方式编号的字符串,例如 SHARD-1-NODE-1,并计算所有 X×N 个字符串的所有 hash 值。

2. 将所有计算出来的 hash 值放到一个排序的 Map 中,并将其中的所有元素进行排序。

3. 输入字符串的时候计算输入字符串的 hash 值,查看 hash 值介于哪两个元素之间,取小于 hash 值的那个元素对应的分片为数据的分片。

跳跃法(jumpstringhash)跳跃法的原理如下:1. 根据公式:

将数据落在每一个节点的概率进行平均分配。

2. 对于输入的字符串进行计算 hash 值,通过判断每次产生的伪随机值是否小于当前判定的节点 1/x,最终取捕获节点编号最大的作为数据的落点。3. 在实际使用中使用倒数的方法从最大节点值进行反向判断,一旦当产生的伪随机值大于 x 则判定此节点 x 作为数据的落点。

数据比较

下面将通过测试对环割法和跳跃法的性能及均衡性进行对比,说明 DBLE 为何使用跳跃法代替了环割法。

    数据源:现场数据 350595 条

    测试经过:

    1. 通过各自的测试方法执行对于测试数据的分片任务。

    2. 测试方法:记录分片结果的方差;记录从开始分片至分片结束的时间;记录分片结果与平均数的最大差值。

    3. 由于在求模法 PartitionByString 的方法中要求分片的数量是 1024 的因数,所以测试过程只能使用 2 的指数形式进行测试,并在 PartitionByString 方法进行测试的时候不对于 MAC 地址进行截断,取全量长度进行测试。

参考技术A 一致性哈希(Consistent Hashing)和数据一致性没有任何关系,这是个关键的理解错误。
一致性哈希只是保证在分布式结构下,哈希结果不会因为某个 node 挂掉而使得所有的键都不能用。在你的图里面,如果 node2 挂掉了,且没有什么自动错误恢复机制存在的话,读写 node2 的键会失败而不是自动落到 node4 上面,所以不存在数据是否一致的问题本回答被提问者采纳

一致性哈希算法

1 hash算法在RPC中的应用和缺陷

 

【背景】:我们有一个社交网站,需要使用Redis存储图片资源,存储的格式为键值对,其中,key为图片名称,value为该图片所在文件服务器路径,我们需要根据文件名查找该文件所在文件服务器上的路径以实现访问并传递给用户,数据量大概200w左右,规则就是随机分配,我们可以部署8台服务器,为保证Redis的高可用,通常我们会做组成4组master-slave的形式,从而实现主机挂掉时秒级切换为备机,使得业务不受影响。

 

  1. 技术图片

【结果】:由于规则随机,一条数据可能存储在任何一组Redis中,因此对于任意一张图片,我们不确定具体是在哪一个Redis服务器上,因此我们最多需要对所有服务器进行遍历才能查询到,这显然不是我们期望的结果。

 

【改进】:采用简单的取模的Hash方式(hash(图片名称)%N),每一张图片在进行分库时都可以定位到特定的服务器。因此对于任意一张图片既可在存储时定位出其要存储的服务器位置,也可在访问时直接定位其位置,如果在指定服务器上没有该图片,说明必然没有缓存,此时应直接请求后端服务器。从而大大的提高性能。

 

【缺陷】:简单取模的hash方式对于服务器数量的变化十分敏感,具体体现在,当服务器中某一组出现故障无法提供服务或者增加一组服务缓存器提供服务时,取模的hash算法的分母发生变化,则由图片名计算出来的服务器组的位置必然发生变化,从而导致大量的缓存在同一时间失效,造成了缓存的雪崩,此时会有大量的请求直接打到后端服务器,后端服务器很有可能因承受不住巨大的压力从而被压垮。

 

为了避免因服务器变动引起的缓存雪崩问题,一致性哈希算法应用而生。

 

2 一致性哈希算法

 

【数据与服务器映射规则定义】:

 

  •  求出服务器的哈希值,并将其映射到0~2^32的圆上

                       hash(服务器名或IP) % 2^32

  • 采用同样的方式求出存储数据的key的哈希值,并映射到相同的圆上

                       hash(key) % 2^32

  • 从数据映射的位置开始顺时针查找,将数据保存到找到的第一个服务器上,如果超过2^32仍然找不到服务器,则将数据保存到第一个服务器上。

 

技术图片

当在上述状态图中添加一台服务器时,余数式分布式算法势必会因为模的变动导致缓存失效,但 一致性哈希算法中,只有增加服务器地点顺时针方向的第一台服务器上的键会受到影响,将本应打到该服务器上的部分或全部数据打到本服务器上。如下图所示:

 

技术图片

 

对于服务器组故障的分析同理。

 

综上可知,一致性哈希算法能够保证当系统的节点发生变化时仍然能够很好的对外提供良好的服务,并且只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

 

3 数据倾斜

 

假设只有两个节点A和B,而所有数据都分布在A的逆时针方向,那么所有的数据都会打到A上,相当于B服务器并没有起到作用,这就是一致性哈希算法在使用过程中的因节点分布不均匀导致的数据倾斜的问题。为了解决这一问题,一致性哈希算法引入虚拟节点机制,即对每一个服务器节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点,具体的做法可以在服务器ip或主机名后增加编号来实现,从而保证服务器节点在哈希环上均匀分布,从而降低了大量数据只打到其中一台服务器上的概率。

 

4 一致性哈希算法的实现

实现的思路比较简单,通过TreeMap建立圆环,并使用默认升序作为排序规则,在确定服务器的过程中每次截取比当前key值大的区间以获取距离最近的服务器位置,若区间内不存在服务器则取第一个服务器。Demo如下所示:

class ConsistantHash {

    static class Node {
        private int nodeNum;

        public Node(int num) {
            this.nodeNum = num;
        }

        @Override
        public String toString() {
            return String.valueOf(nodeNum);
        }
    }

    /**
     * 圆环(用treeMap的主要原因是可以排序)
     */
    private TreeMap<Long, Node> circle = new TreeMap<Long, Node>();


    /**
     * 添加节点到圆环中
     * */
    public void addNode(Node node) {
        Long nodeKey = md5(node.toString());
        System.out.println(node.toString() + " md5:" + nodeKey);
        circle.put(nodeKey, node);
    }

    public static void main(String[] args) {
        ConsistantHash h = new ConsistantHash();
        /**
         * 添加节点服务器
         * */
        h.addNode(new Node(10));
        h.addNode(new Node(30));
        h.addNode(new Node(60));
        /**
         * 设定节点,确定其服务器位置
         * */
        for (int i = 0; i < 50; i++) {
            System.out.println(i + "--->服务器节点" + h.getNode("" + i));
        }
    }

    /**
     * 设置节点服务器分配规则
     * */
    public Node getNode(String key) {

        /**
         * treemap 转成 排序好的map
         * */
        Long keyMd5 = md5(key);
        System.out.println("key is:" + key + ", keyMd5 is:" + keyMd5);

        /**
         * 截取key大于等于keyMd5的所有元素, 因为正常情况下 md5(key) =< md5(node)
         * */
        SortedMap<Long, Node> sortedMap = circle.tailMap(keyMd5);
        Long k;

        /**
         * 没命中则选择第一个节点并直接返回
         * */
        if (sortedMap.isEmpty()) {
            System.out.println("not hit and choose the first node");
            k = (circle.firstKey());
        } else {
            /**
             * 命中则选择最近的一个节点
             * */
            k = (sortedMap.firstKey());
        }

        Node node = circle.get(k);

        System.out.println(key + "(" + keyMd5 + ") --->" + node.toString() + "(" + md5(node.toString()) + ")");

        return node;
    }


    /**
     * 确定加密方式
     * */
    private long md5(String key) {
        Long nodeKey = 0L;
        int numOfTen = key.toCharArray().length - 1;
        for (char it : key.toCharArray()) {
            if (numOfTen > 0) {
                for (int i = 0; i < numOfTen; i++) {
                    nodeKey = nodeKey + Long.valueOf(it + "") * 10;
                }
            } else {
                nodeKey = nodeKey + Long.valueOf(it + "");
            }
            numOfTen--;
        }
        return nodeKey;
    }

}

 

参考:https://www.cnblogs.com/study-everyday/p/8629100.html

参考:https://www.cnblogs.com/lpfuture/p/5796398.html

 

 

以上是关于一致性哈希算法怎么保证数据的一致性的主要内容,如果未能解决你的问题,请参考以下文章

hashMap怎么保证hash一致性的

简明讲解一致性哈希算法

一致哈希算法Java实现

面试官问:一致性哈希算法是什么?怎么判定哈希算法的好坏?

一致性哈希算法

一致性哈希算法