【问题标题】:Can circular hashing remain consistent as the target set grows?随着目标集的增长,循环哈希能否保持一致?
【发布时间】:2013-03-08 03:15:49
【问题描述】:

循环散列算法在给定一组静态目标的情况下提供一致性。例如:

  1. 我有一组初始目标,我们称它们为 ABC
  2. 我有钥匙,我们就叫它x
  3. 我有一个循环哈希函数,我们称之为hash(key, targets)
  4. 当我调用hash(x, [A,B,C]) 时,x 总是哈希到A

似乎很明显。给定x,我总是得到A,这一事实代表了我在使用循环哈希时所期望的一致性。但是,现在让我们考虑如果我添加一个新的会发生什么 节点D:

  1. 我的目标集重新平衡以包括ABCD
  2. 我将密钥x 重新应用到hash(x, [A,B,C,D])
  3. 因为圈子重新平衡了,我不能保证再得到A

我是错过了什么,还是只是运气不好?当您开始重新排序节点(例如hash(x, [B,A,D,C]))或在现有节点列表的中间插入新节点(例如hash(x, [A,AA,B,C,D]))时,问题会进一步加剧。我对循环散列的学术方面进行了一些研究,这种类型的“缩放一致性”似乎并不是它的主要关注点之一。也许我只是使用了错误类型的哈希算法?

【问题讨论】:

  • 抱歉,我找不到很多关于循环哈希的资料。你的意思是一致性哈希和 DHT?
  • 您的回答恰当地解释了我所理解的“循环散列”。但是,我认为我离真正的解决方案还差得远。用“记忆”的概念很容易解决这个问题,即记住过去的键在哪里散列。但是,将内存构建到算法中可能是不可能的。在对密钥进行哈希处理后,算法必须发生变化。添加新目标不应影响过去密钥的散列位置。
  • “因为循环重新平衡,我不能保证再得到 A” 所以你是说对于已经散列的项目,它们不应该被分发到新节点? /“当您开始重新排序节点时,问题会进一步加剧”但是在我在答案中提出的模型中,没有重新排序节点这样的事情,因为节点集是无序的。你能解决这两个问题吗?
  • 当然。因此,对于您关于已散列项目的问题,是的,它们永远不应该散列到新节点。换句话说,一旦一个键被散列到一个节点,它应该总是散列到那个节点。对于您的第二个问题,您说得对,顺序不会影响您的算法。但是,在我尝试完成这项工作时,我使用了有序列表,甚至围绕圆圈分布。因此,即使添加了新节点,为了保持弧线,现有节点都必须移动(即“重新平衡”)。希望这会增加更多的清晰度,而不是更少:)

标签: php javascript scalability consistent-hashing


【解决方案1】:

您的问题有一个非常简单的解决方案。这是它如何工作的示例。

假设您有 3 个真实目标(即物理机):A、B、C。然后您引入 9 个虚拟目标:1、2、3、4、5、6、7、8、9 并建立静态映射像这样从虚拟目标到真实目标:

1, 2, 3 -> A
4, 5, 6 -> B
7, 8, 9 -> C

当您需要读取/写入某个键的值时,您首先使用哈希函数将键映射到虚拟目标,然后使用上面显示的静态映射将虚拟目标映射到真实目标。一旦某个真实目标服务于多个虚拟目标,它应该将它们存储在单独的哈希映射中,因此真实目标 B 为其服务的三个虚拟目标具有三个独立的哈希映射。

现在我们要添加新的真实目标: D. 我们首先重新平衡我们的静态映射,例如像这样:

1, 2, 3 -> A
4, 5 -> B
7, 8 -> C
6, 9 -> D

然后我们将服务于虚拟目标6 的哈希映射从真实目标B 转移到新的真实目标D。我们还将地图服务虚拟目标9C 转移到D。此操作具有复杂性O(n),其中n 是传输的值的数量,因为每个真实目标都在单独的哈希映射中为每个虚拟目标服务。

为了实现良好的负载平衡,虚拟目标的数量应该比实际目标的最大可能数量估计值大几倍(例如 10 倍)。

换句话说,该解决方案的主要思想是使用哈希函数将键映射到虚拟目标的数量不变的虚拟目标。然后使用静态映射将虚拟目标映射到真实目标,并且在添加或删除真实目标时,此静态映射会发生变化。

【讨论】:

  • 我认为这行不通。原因如下:使用您的第一个示例,假设 x 散列到虚拟目标 6 散列到实际目标 C。现在让我们重新平衡,就像你的第二个例子一样。在重新平衡之后,x 仍然映射到虚拟目标6,这实际上是一致的。但是,虚拟目标6 不再映射到真实目标C。虽然我可能获得了更好的分布,但我未能保持x 和实际目标C 之间的相关性。当我们添加新目标时,保持从关键到真实目标的相关性是我的目标;没有更好的分布。
  • 正如我之前提到的,再平衡可能需要在真实目标之间移动虚拟目标,这意味着将包含所有数据的整个哈希映射从一个真实目标转移到另一个真实目标。
【解决方案2】:

当您扩展散列函数的允许输出范围时,理所当然的一些输入将散列到不同的输出(否则扩展范围没有意义)。唯一的方法是哈希函数存储所有以前的结果(或压缩的,可能有损的相同形式,如布隆过滤器),以便它可以记住将“旧”结果用于它的输入以前见过。

【讨论】:

  • 一个完全公平的回应。但是,我不想让算法记住任何东西。
【解决方案3】:

我无法以一致的方式解释您的整个问题,所以我会据此猜测您真正想问和回答的问题。

假设的问题:您有一堆对象(例如字符串)和一堆机器,并且您希望将每个对象分配给一台机器,以便在机器之间分散工作量。当机器加入或离开机器池时,您不希望重新调整太多的对象到机器分配(“缩放一致性”)。

我认为你有一个误解,你说你散列对象x 以映射池中的一台机器[A,B,C]。我的理解是涉及三个中间步骤。

  1. 计算每个对象的哈希值。假设哈希输出空间很大,比如从 0 到 232 - 1 的所有整数。

  2. 为每台机器分配一个值(在相同的数字空间中),该值在其生命周期内保持不变。您可能希望将这些数字随机分布。

  3. 现在,我们将每个对象分配给最近的向上机器。这意味着如果对象的哈希是 x,那么它属于机器 M,使得 M 的值是大于 x 的最小数。

例子:

  1. 我们有 4 个字符串对象,它们各自的哈希值在 0 到 999 范围内:abc=314、def=125、ghi=802、jkl=001。

  2. 我们有 3 台机器,编号为:X=010、Y=357、Z=768。

  3. 对象 abc=314 属于哪台机器?往上数,最近的机器是Y=357。
    对象 ghi=802 属于哪台机器?往上数,最近的机器是X=010。

【讨论】:

  • 这个响应没有任何问题。你对我的意思的假设是完全正确的。但是,我不确定这是否真的回答了我的问题。
【解决方案4】:

好的,我想我明白了。

我最终使散列算法保持简单,并使用“校验和”(各种)来确保 x 始终键为同一个目标。当添加新目标并且系统重新平衡时,我只需通知所有现有目标有关重新平衡的信息。这样,如果 x 散列到不应再散列到的目标,则目标可以委托给正确的目标。

感谢您的所有回复,如果不是因为你们提供的清晰性,我可能不会想到这个解决方案。

干杯,

乔恩

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    相关资源
    最近更新 更多