分布式系统设计理论之一致性哈希

简介: 分布式系统设计理论之一致性哈希

分布式系统设计理论之一致性哈希

主要参考和围绕这篇论文讲解:Consistent Hashing

1 问题引入

什么是一致性哈希?为什么要用一致性哈希?

2 一致性哈希算法出现之前的分布式系统设计

例如系统需要构建分布式缓存,多个节点分别部署而形成的一个分布式集群,当有请求到来时进行负载均衡,具体的负载均衡方式就是将节点的ID(唯一标识)进行哈希值的取余运算,得到结果的机器就是进行请求处理的机器。

上述方式存在的问题

当一个节点进行更换(增加或删除节点)时,原有的节点消失或增加新的节点,会导致多个请求同时指向一个节点进而造成节点的宕机等问题。

总之一句话就是,当服务器个数发生改变时,缓存可能会失效。

图解:

正常情况下:

删除一个服务器之后:

3 什么是一致性哈希,它是如何解决上述问题的?

3.1 一致性哈希的核心思想

⼀致性哈希算法的基本思想是使⽤相同的哈希函数服务器节点对象和请求的缓存进⾏哈希,即⼀致地将对象映射到相同的缓存机器。

3.2 引入哈希环

整数int在Java中的取值范围是2的-31次方到2的31次方-1之间,那么就可以让请求的缓存和服务器节点同时进行相同的哈希算法后得到一个int值,进而形成一个哈希环,请求和服务器节点保持“顺时针就近”原则进行对应的请求:

A,B,C代表三个服务器节点,1,2,3,4代表着不同的缓存请求,按照“顺时针就近”原则,1,4会请求服务器A节点,2请求服务器B节点,3请求服务器C节点。

3.3 存在问题
(1)增删节点带来的问题

新增服务器节点D,按照之前的原则4就会向服务器D节点发起请求,在4和D之间的一部分缓存将会失效,这是问题之一。

(2)哈希倾斜

所谓哈希倾斜就是表示如上图,服务器A,B相距较近,假如没有服务器D节点,那么B到A这一段的请求都会落在A节点上,即大部分请求都指向A节点,而极少部分请求放在了B节点,这个问题就是“哈希倾斜”。

3.4 增加虚拟节点解决问题

解决上边两个问题的最佳方案就是增加虚拟节点,就是在哈希环中增加无数个虚拟节点,请求指向虚拟节点,虚拟节点指向真实节点,这样就会增加请求平均的概率,如图:

而且论文中还指出,虚拟节点的数量越大,请求产生的偏差越小:

4 Java代码实现一致性哈希

/**
 * @desc: Java实现一致性哈希
 * @author: YanMingXin
 * @create: 2021/10/16-16:46
 **/
public class ConsistentHash<T> {
    /**
     * 哈希函数类
     */
    private final HashFunction hashFunction;
    /**
     * 虚拟节点个数(不同节点一共的,会自动平均)
     */
    private final int numberOfReplicas;
    /**
     * 哈希环
     */
    private final SortedMap<Integer, T> circle = new TreeMap<Integer, T>();
    /**
     * 构造方法
     *
     * @param hashFunction     自定义哈希函数类(必须有hash()方法)
     * @param numberOfReplicas 副本个数
     * @param nodes            服务器节点
     */
    public ConsistentHash(HashFunction hashFunction,
                          int numberOfReplicas, Collection<T> nodes) {
        this.hashFunction = hashFunction;
        this.numberOfReplicas = numberOfReplicas;
        for (T node : nodes) {
            add(node);
        }
    }
    public ConsistentHash(int numberOfReplicas, Collection<T> nodes) {
        this.hashFunction = new HashFunction();
        this.numberOfReplicas = numberOfReplicas;
        for (T node : nodes) {
            add(node);
        }
    }
    /**
     * 向哈希环增加节点的副本
     *
     * @param node
     */
    public void add(T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            circle.put(hashFunction.hash(node.toString() + i), node);
        }
    }
    /**
     * 删除节点
     *
     * @param node
     */
    public void remove(T node) {
        for (int i = 0; i < numberOfReplicas; i++) {
            circle.remove(hashFunction.hash(node.toString() + i));
        }
    }
    /**
     * 根据请求得到他将要访问的节点
     *
     * @param key
     * @return
     */
    public T get(Object key) {
        if (circle.isEmpty()) {
            return null;
        }
        //将请求hash转换
        int hash = hashFunction.hash(key);
        //将请求放入哈希环
        if (!circle.containsKey(hash)) {
            //返回该映射中键值大于或等于hash的部分的视图。 
            SortedMap<Integer, T> tailMap = circle.tailMap(hash);
            //判断tailMap是否为空,是则返回哈希环第一个节点的值,反之则返回最近的节点
            hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
        }
        return circle.get(hash);
    }
    /**
     * 哈希函数类
     */
    class HashFunction {
        /**
         * 具体的哈希函数
         *
         * @param key
         * @return
         */
        public int hash(Object key) {
            return key.hashCode() / Integer.MAX_VALUE;
        }
    }
}

部分图片和内容来自:http://tom-e-white.com/2007/11/consistent-hashing.html

相关文章
|
21天前
|
缓存 网络协议 算法
Golang简单实现 分布式缓存+一致性哈希+节点再平衡(gossip + consistent + rebalance)
Golang简单实现 分布式缓存+一致性哈希+节点再平衡(gossip + consistent + rebalance)
84 0
|
21天前
|
存储 数据采集 缓存
哈希表、分布式一致性哈希及布隆过滤器详解
哈希表、分布式一致性哈希及布隆过滤器详解
|
缓存 负载均衡 算法
分布式系统设计理论之一致性哈希
分布式系统设计理论之一致性哈希
|
21天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
320 2
|
2天前
|
缓存 NoSQL 关系型数据库
【Redis】 浅谈分布式架构
【Redis】 浅谈分布式架构
|
8天前
|
存储 NoSQL 算法
Redis (分布式锁)
Redis (分布式锁)
189 0
|
21天前
|
存储 监控 NoSQL
【Redis】分布式锁及其他常见问题
【Redis】分布式锁及其他常见问题
264 0
|
21天前
|
NoSQL Java Redis
【Redis】Redis实现分布式锁
【Redis】Redis实现分布式锁
23 0
|
21天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
203 16
探秘Redis分布式锁:实战与注意事项