分布式哈希表(Distributed Hash Table, DHT)是分布式可伸缩系统的基本组件之一。哈希表需要键、值和哈希函数,哈希函数将键映射到存储值的位置。
index = hash_function(key)
假设我们正在设计一个分布式缓存系统。给定' n '缓存服务器,直观的哈希函数将是' key % n '。它简单而常用。但它有两个主要缺点:
1.它不是水平伸缩的。每当向系统添加一个新的缓存主机时,所有现有的映射都会被破坏。如果缓存系统包含大量的数据,这将是维护中的一个痛点。实际上,很难安排停机时间来更新所有缓存映射。
2.它可能不是负载均衡的,特别是对于非均匀分布的数据。在实践中,很容易假设数据不是均匀分布的。对于缓存系统来说,这意味着一些缓存变得热且饱和,而其他缓存则闲置且几乎为空。
在这种情况下,一致的哈希是改进缓存系统的好方法。
对于分布式缓存系统和dht,一致哈希是一种非常有用的策略。它允许以这样一种方式在集群中分布数据,从而将添加或删除节点时的重组最小化。因此,让缓存系统更容易扩展或缩小。
在一致哈希中,当哈希表的大小被调整时(例如一个新的缓存主机被添加到系统中),只需要映射kn键,其中k是键的总数,n是服务器的总数。回想一下,在使用“mod”作为哈希函数的缓存系统中,所有的键都需要被映射。
在一致的哈希中,如果可能,将对象映射到相同的主机。当主机从系统中移除时,该主机上的对象将被其他主机共享;当一个新主机被添加时,它会从几个主机中获取它的份额,而不会触及其他主机的份额。
作为一个典型的哈希函数,一致的哈希将一个键映射为一个整数。假设哈希函数的输出在[0,256]的范围内。假设这个范围内的整数被放置在一个环上,这样值就被包围起来了。
以下是一致哈希的工作原理:
- 给定一个缓存服务器列表,将它们散列为范围内的整数。
- 要将密钥映射到服务器,
- 将其散列为单个整数。
- 顺时针方向移动,直到找到它遇到的第一个缓存。
- 这个缓存就是包含键的那个。下面的动画就是一个例子:key1映射到缓存A;key2映射到缓存C。
为了添加一个新的服务器,比如D,原来驻留在C的键将被拆分。其中一些键将被移到D,而其他键将不会被触摸。
为了移除缓存,或者如果缓存失败,比如a,所有原映射到a的键都将落入B,只有那些键需要移动到B,其他键不会受到影响。
对于负载平衡,正如我们在开始时讨论的那样,实际数据基本上是随机分布的,因此可能不是均匀的。它可能使缓存上的键不平衡。
为了处理这个问题,我们为缓存添加了“虚拟副本”。我们不是将每个缓存映射到环上的单个点,而是映射到环上的多个点,即副本。这样,每个缓存都与环的多个部分相关联。
如果哈希函数“混合得很好”,随着副本数量的增加,键将更加平衡。