关于一致性Hash算法在实际生产中的应用

关于一致性Hash算法在实际生产中的应用

一.背景

公司原先所有业务的数据都存储在一个相同的Redis集群里面,导致数据量太大,后续扩容不好扩,所以现在需要做的是将不同业务进行拆分,不同业务的数据存储到不同的Redis集群中。所以有以下实际。

二.思考

我们都知道这个算法的由来是因为在大数据时代,一个节点存储的数据量是有限的,系统的访问也是有瓶颈的,像一般数据库都是支持sharding的,也就是分片,来提高数据库访问的承受能力elasticsearch中使用的是分片,mongoDB中使用的也是sharding的概念,Redis集群模式使用的是哈希槽的方式,但是万变不离其宗,采用的思想都是路由的思想。

三.思考过程

3.1 是否可以直接使用Hash取模的方式来实现该需求呢?

其实经过思考,在本场景是可以满足需求的,因为我们是以集群为单位的,一旦集群建立好了就不会再有变动,并且一个集群中的节点不可能全部死掉,所以就不存在拆分数据后找不到数据的原因。但是使用Hash取模的方式存在哪种缺陷呢,假设是以节点为单位来实现hash取模存储,如果一个集群中的节点死掉了或者添加一个节点,导致集群中的节点个数减少了,取模后定位到的节点也会发生变化,从而导致在高峰期,在缓存中找不到数据而直接打到DB上而造成缓存雪崩,从而打垮服务。

3.2为什么还要采用一致性Hash来实现

为了长远考虑,因为谁也不知道过个三年五年是否还会添加集群,到时候还要预热多个集群的数据,采用一致hash,可以减少需要预热的集群的数据。

3.3.一致性hash原理

哈希函数实际上将对象和缓存映射到一个数字范围,Object的hashCode方法返回一个int,它位于它位于 -2 31到 2 31 -1 的范围内,将这个范围映射成一个圆圈。如下图

在这里插入图片描述
上图的 1,2,3,4代表的是缓存的数据,而A,B,C代表的是缓存的节点,当我们沿着顺时针查找的第一个节点就是我们需要存储的节点,从上图我们可以知道1,4属于A节点,2,属于B节点,3属于C节点,如果C节点被删除,此时3也属于A节点,若添加一个节点D这此时3,4属于D节点,而1属于A节点,如下图
在这里插入图片描述
但是这样做有一个缺陷,因为我们的节点个数比较少,可能导致数据集中在某些节点上,而发生数据倾斜,所以就有了另外一个概念,虚拟节点,增加虚拟节点的目的主要是为了让数据分配更加的均匀。

四.代码实现

4.1定义一致性hash算法工具类

public class ConsistentHashUtil<T> {
    //hash算法实现
    private final HashFunction hashFunction;
    //一个节点对应的虚拟节点个数
    private final  int numberOfReplicas;
    //存储节点的容器
    private final SortedMap<Integer,T> circle=new TreeMap<>();

    public ConsistentHashUtil(HashFunction hashFunction, int numberOfReplicas, Collection<T> nodes) {
        this.hashFunction = hashFunction;
        this.numberOfReplicas = numberOfReplicas;
        for (T node : nodes) {
            add(node);
        }
    }

    private void add(T node){
        for (int i = 0; i < numberOfReplicas; i++) {
            //使用hash值作为key,真实节点为node
            circle.put(hashFunction.hash(node.toString()+i),node);
        }
    }
    private void remove(T node){
        for (int i = 0; i < numberOfReplicas; i++) {
            circle.remove(hashFunction.hash(node.toString()+i));
        }
    }

    public T get (Object key){
        if(circle.isEmpty()){
            return null;
        }
        int hash =hashFunction.hash(key);
        //判断是否存在这个key
        if(!circle.containsKey(hash)){
            //获取大于这个hash值得所有节点
            SortedMap<Integer, T> tailMap = circle.tailMap(hash);
            //判断是否为空,若为空则那所有节点中的第一个hash值,否则拿获取到的第一个
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        //获取真实节点
        return circle.get(hash);
    }
}

4.2定义一个函数式接口用来满足不同的hash算法

@FunctionalInterface
public interface HashFunction<T> {
    int hash(T value);
}

4.4测试

public class ConsistentHashTest {
    private static String[] servers = {"cluster1","cluster2","cluster3"};
    private static final int VIRTUAL_NODES=100;
    public static void main(String[] args) {
        ConsistentHashUtil<String> consistentHashUtil = new ConsistentHashUtil<>(T -> {
            String temp = (String) T;
            final int p = 16777618;
            int hash = (int) 2166136261L;
            for (int i = 0; i < temp.length(); i++) {
                hash = hash ^ temp.charAt(i) * p;
                hash += hash << 13;
                hash ^= hash >> 7;
                hash += hash << 3;
                hash ^= hash >> 17;
                hash += hash << 5;
            }
            if (hash < 0) {
                hash = Math.abs(hash);
            }
            return hash;
        }, VIRTUAL_NODES, Arrays.asList(servers));
        //重复获取十次判断是否拿到的集群是同一个
        for (int i = 0; i < 10; i++) {
            String result = consistentHashUtil.get("ljm");
            System.out.println(result);
        }
    }
}

4.5测试结果,从结果中我们可以看出拿到的结果都是cluster3,算法有效

在这里插入图片描述

五.总结

本场景中将原先的以节点为单位适用的一致性hash函数,引入到以集群为单位的场景中同样适用,一致性hash的概念没有阐述太多,具体的hash算法MD5,RSA,sha-1等算法,若自己公司想要用自己的算法,可以实现HashFunction这个函数式接口,也可以直接使用lambada表达式直接使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值