`
jag522
  • 浏览: 33814 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

一致性哈希算法

阅读更多
分布式缓存问题

假设我们有一个网站,最近发现随着流量的增加,服务器压力越来越大,之前直接读写数据库的方式不太给力了,于是我们想引入Memcached作为缓存机制。现在我们一共有三台机器可以作为Memcached服务器,如下图所示。


很显然,最简单的策略是将每一次Memcached请求随机发送到一台Memcached服务器,但是这种策略可能会带来两个问题:一是同一份数据可能被存在不同的机器上而造成数据冗余,二是有可能某数据已经被缓存但是访问却没有命中,因为无法保证对相同key的所有访问都被发送到相同的服务器。因此,随机策略无论是时间效率还是空间效率都非常不好。

要解决上述问题只需做到如下一点:保证对相同key的访问会被发送到相同的服务器。很多方法可以实现这一点,最常用的方法是计算key的哈希值,再对哈希值取模。公式为:
h = Hash(key) % N

其中h是计算出来的每个key的请求应该被发送到哪台服务器的服务器编号;Hash()是一个从字符串到正整数的哈希映射函数,常见的哈希算法有MurMurHash, MD5等;N是服务器的个数,服务器按照0 ~ (N-1)编号。

这样,如果我们将Memcached Server分别编号为0、1、2就可以解决上面提到的两个问题,但是还存在着一些其它问题。

这个算法的问题在于容错性和扩展性不好。所谓容错性是指当系统中某一个或几个服务器变得不可用时,整个系统是否可以正确高效运行;而扩展性是指当加入新的服务器后,整个系统是否可以正确高效运行。

现假设有一台服务器宕机了,那么为了填补空缺,要将宕机的服务器从编号列表中移除,后面的服务器按顺序前移一位并将其编号值减一,此时每个key所在的缓存服务器就要按h = Hash(key) % (N-1)重新计算;同样,如果新增了一台服务器,虽然原有服务器编号不用改变,但是要按h = Hash(key) % (N+1)重新计算,大量的key会被重定位到不同的服务器从而造成大量的缓存不命中。而这种情况在分布式系统中是非常糟糕的。

一个设计良好的分布式哈希方案应该具有良好的单调性,即服务节点的增减不会造成大量哈希重定位。一致性哈希算法就是这样一种哈希方案。

一致性哈希算法

算法简述
一致性哈希算法(Consistent Hashing)最早在论文《Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web》中被提出。简单来说,一致性哈希将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0 ~ 232-1(即哈希值是一个32位无符号整型),整个哈希空间环如下:


整个空间按顺时针方向组织。0和232-1在零点钟方向重合。

下一步可以选择服务器的IP地址或主机名作为关键字,使用Hash()函数将各个服务器进行哈希,这样每台机器就能确定其在哈希环上的位置,这里假设将上述三台服务器使用IP地址哈希后在环空间的位置如下:


接下来使用如下算法定位数据访问到相应服务器:
将数据key使用相同的哈希函数Hash()计算出哈希值h,根据h确定此数据在环上的位置,从该位置沿着环以顺时针方向“行走”,第一台遇到的服务器就是其应该定位到的服务器。如果哈希值h超过232-1,则从0开始定位到顺时针方向最近的服务器。


例如我们有A、B、C、D四个数据对象,经过哈希计算后,在环空间上的位置如下:


根据一致性哈希算法,数据A会被定为到Server 1上,D被定为到Server 3上,而B、C分别被定为到Server 2上。

容错性与可扩展性分析
下面分析一致性哈希算法的容错性和可扩展性。现假设Server 3宕机了:


可以看到此时A、C、B不会受到影响,只有D节点被重定位到Server 2。一般的,在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即顺着逆时针方向行走遇到的第一台服务器)之间的数据,其它不会受到影响。

下面考虑另外一种情况,如果我们在系统中增加一台服务器Memcached Server 4:


此时A、D、C不受影响,只有B需要重定位到新的Server 4。一般的,在一致性哈希算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器到其环空间中前一台服务器(即顺着逆时针方向行走遇到的第一台服务器)之间的数据,其它不会受到影响。

综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

虚拟节点
一致性哈希算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜问题。例如我们的系统中有两台服务器,其环分布如下:


此时必然造成大量数据集中到Server 1上,而只有极少量会定位到Server 2上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP地址或主机名的后面增加编号来实现。例如上面的情况,我们决定为每台服务器计算三个虚拟节点,于是可以分别计算“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”、“Memcached Server 2#1”、“Memcached Server 2#2”、“Memcached Server 2#3”的哈希值,于是形成六个虚拟节点:


同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”三个虚拟节点的数据均定位到Server 1上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 一致性哈希算法 简单示例
 */
public class ConsistentHash<T> {

    private final Hashing algo; // 哈希函数
    private final int virtualNodeNum = 32; // 虚拟节点数量
    private final SortedMap<Long, T> circleMap = new TreeMap<Long, T>(); // 哈希环

    public ConsistentHash(Hashing algo, List<T> nodes) {
        this.algo = algo;
        for (T node : nodes) {
            add(node);
        }
    }

    public void add(T node) {
        for (int i = 0; i < virtualNodeNum; i++) {
            circleMap.put(algo.hash(node.toString() + i), node);
        }
    }

    public void remove(T node) {
        for (int i = 0; i < virtualNodeNum; i++) {
            circleMap.remove(algo.hash(node.toString() + i));
        }
    }

    public T get(Object key) {
        if (circleMap.isEmpty()) {
            return null;
        }
        SortedMap<Long, T> tailMap = circleMap.tailMap(algo.hash(key.toString()));
        if (tailMap.size() == 0) {
            return circleMap.get(circleMap.firstKey());
        } else {
            return circleMap.get(tailMap.firstKey());
        }
    }

    public static void main(String[] args) {
        Node node1 = new Node("node1", "192.168.0.1");
        Node node2 = new Node("node2", "192.168.0.3");
        Node node3 = new Node("node3", "192.168.0.5");
        List<Node> nodeList = Arrays.asList(node1, node2, node3);
        ConsistentHash<Node> consistentHash = new ConsistentHash<Node>(new Hashing(), nodeList);
        System.out.println(consistentHash.get("134"));
        System.out.println(consistentHash.get("863"));
        System.out.println(consistentHash.get("048"));
        System.out.println(consistentHash.get("652"));
        System.out.println(consistentHash.get("862"));
    }
}

class Node {
    private String name;
    private String ip;
    public Node(String name, String ip) {
        this.name = name;
        this.ip = ip;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getIp() {
        return ip;
    }
    public void setIp(String ip) {
        this.ip = ip;
    }
    @Override
    public String toString() {
        return ip;
    }
}

class Hashing {
    public ThreadLocal<MessageDigest> md5Holder = new ThreadLocal<MessageDigest>();
    public long hash(String key) {
        try {
            return hash(key.getBytes("UTF-8"));
        } catch (Exception e) {
            throw new RuntimeException();
        }
    }

    public long hash(byte[] key) {
        try {
            if (md5Holder.get() == null) {
                md5Holder.set(MessageDigest.getInstance("MD5"));
            }
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("No MD5 algorithm found");
        }
        MessageDigest md5 = md5Holder.get();

        md5.reset();
        md5.update(key);
        byte[] bKey = md5.digest();
        long res = ((long) (bKey[3] & 0xFF) << 24) | ((long) (bKey[2] & 0xFF) << 16) | ((long) (bKey[1] & 0xFF) << 8)
                | (long) (bKey[0] & 0xFF);
        return res;
    }
}


参考资料:
http://blog.codinglabs.org/articles/consistent-hashing.html
  • 大小: 13.8 KB
  • 大小: 20.9 KB
  • 大小: 24 KB
  • 大小: 21.6 KB
  • 大小: 26.6 KB
  • 大小: 19.4 KB
  • 大小: 31.1 KB
  • 大小: 18.9 KB
分享到:
评论
1 楼 yunchow 2014-09-23  
1,一致性hash主要通过环形hash满足单调性,通过虚拟节点满足平衡性,通过对key进行MD5编码取后4个字节来实现分散性,是一个非常成功的分布式hash算法
2,TreeMap的使用很经典,大大简化了代码

相关推荐

    一致性哈希算法(ketama hashing)

    一致性哈希算法(Consistent Hashing)是一种在分布式系统中实现负载均衡的算法,尤其在分布式缓存如Memcached和Redis等场景下广泛使用。它解决了传统哈希算法在节点增减时导致的大量数据迁移问题,提高了系统的可用...

    基于一致性哈希算法的分布式数据库高效扩展方法.pdf

    一致性哈希算法最初由麻省理工学院的K等人提出,并被广泛应用于分布式系统中,以解决节点动态变化时数据一致性问题。其核心思想是通过引入哈希环,将数据对象均匀分布在哈希环上的不同节点中,以此降低节点变更对...

    一致性哈希算法C版实现

    一致性哈希算法是一种在分布式系统中解决数据分片和负载均衡问题的算法,它主要解决了在动态添加或移除节点时,尽可能少地改变已经存在的数据分布。在云计算和大数据处理领域,一致性哈希被广泛应用,例如在分布式...

    分布式存储系统中改进的一致性哈希算法.pdf

    一致性哈希算法通过将哈希值空间组织成一个虚拟的环状结构,使得每个存储节点仅负责环上的一段区域,从而有效减少了节点变化时的数据迁移量。然而,一致性哈希算法也存在一些问题,比如在节点数量较少时,节点间的...

    基于C# 实现的一致性哈希算法

    一致性哈希算法是一种分布式哈希(Distributed Hash Table, DHT)技术,它解决了在分布式环境中数据分片和负载均衡的问题。在传统的哈希算法中,如果增加或减少服务器节点,会导致大量数据重新分配,而一致性哈希...

    基于一致性哈希算法的分布式数据库高效扩展方法研究.pdf

    【摘要】中的“高效扩展”和“分布式数据库”是本文的核心话题,研究的是如何利用一致性哈希算法在大数据时代高效地扩展分布式数据库。一致性哈希算法最初由Karger等人提出,目的是解决分布式缓存的问题,它弥补了...

    一致性哈希算法演示.rar

    一致性哈希算法是一种分布式哈希表(DHT)中用于解决数据分片和负载均衡问题的算法。在大型分布式系统中,例如缓存系统、分布式数据库等,一致性哈希能够确保当节点加入或离开时,尽可能少的数据需要迁移,从而保持...

    一致性哈希算法源码 Ketama一致性hash算法源码

    一致性哈希算法是一种分布式哈希(Distributed Hash Table, DHT)技术,旨在解决在分布式环境中数据分布不均匀的问题。Ketama算法是基于一致性哈希的一种优化实现,由Last.fm公司的Simon Willison提出,其目标是在...

    一致性哈希算法在分布式系统中的应用.pdf

    一致性哈希算法是一种在分布式系统中用于解决数据分发和负载均衡问题的算法。随着互联网技术的快速发展,分布式系统已经成为支撑大规模服务的关键技术之一。在分布式系统中,多个节点通过网络协同工作,提供高可用性...

    一致性哈希算法及其在分布式系统中的应用

    ### 一致性哈希算法及其在分布式系统中的应用 #### 摘要 一致性哈希算法是一种用于解决分布式系统中节点动态变化导致的数据重新分布问题的关键技术。它通过将哈希空间映射到一个循环的空间中,实现了数据节点的高效...

    一致性哈希算法应用及优化(最简洁明了的教程)

    一致性哈希算法应用及优化是IT领域中分布式系统设计的核心技术之一,特别是在处理大规模数据分布与缓存系统中,其重要性不言而喻。本文将深入探讨一致性哈希算法的基本概念、工作原理以及在实际场景中的应用和优化...

    分布式存储系统中一致性哈希算法的研究.pdf

    一致性哈希算法作为解决这一问题的重要手段之一,近些年来得到了广泛关注和应用。 一致性哈希算法由David Karger等人在1997年提出,它是一种特殊的哈希算法,主要用于分布式系统中实现负载均衡。与传统的哈希算法...

    python 实现 一致性哈希算法

    一致性哈希算法

    白话解析:一致性哈希算法1

    白话解析:一致性哈希算法1 一致性哈希算法是解决分布式缓存问题的解决方案。缓存服务器数量的变化会引起缓存的雪崩,导致整体系统压力过大而崩溃。为了解决这个问题,一致性哈希算法诞生了。 在了解一致性哈希...

    基于NIO-EPOOL模型netty实现的具备一致性哈希算法的NAT端口映射器

    本项目以“基于NIO-EPOOL模型netty实现的具备一致性哈希算法的NAT端口映射器”为主题,深入探讨了Netty在NAT端口映射中的应用,以及一致性哈希算法在此过程中的作用。 首先,我们来了解NIO(Non-blocking I/O,非...

    基于一致性哈希算法的区块链优化模型.pdf

    #资源达人分享计划#

    CHB-Consensus:一种基于一致性哈希算法的区块链共识机制研究.pdf

    #资源达人分享计划#

    分布式存储系统:Cassandra:数据分布与一致性哈希算法.docx

    分布式存储系统:Cassandra:数据分布与一致性哈希算法.docx

Global site tag (gtag.js) - Google Analytics