《系统设计》课程学习笔记—一致性哈希

143 阅读4分钟

让我们先了解我们要解决的问题。

为什么我们需要这个?

在传统的基于哈希的分发方法中,我们使用哈希函数对分区密钥(即请求ID或IP)进行散列。然后,我们对节点总数(服务器或数据库)使用模运算。这会使我们得到要路由请求的节点。

simple-hashing.png

Hash(key1)H1modN=Node0Hash(key2)H2modN=Node1Hash(key3)H3modN=Node2...Hash(keyn)HnmodN=Noden Hash(key_1) \rightarrow H_1modN=Node_0 \\ Hash(key_2) \rightarrow H_2modN=Node_1 \\ Hash(key_3) \rightarrow H_3modN=Node_2 \\ ... \\ Hash(key_n) \rightarrow H_nmodN=Node_n

这里,

key:请求ID或IP。

H: 哈希函数结果。

N: 节点总数。

Node:请求将路由到的节点。 ​ 问题是,如果我们添加或删除节点,它将导致 N 发生变化,这意味着我们的映射策略被破坏,因为相同的请求现在将映射到不同的服务器。因此,大多数请求需要重新分配,这是非常低效的。

我们希望在不同的节点之间均匀分布请求,以便能够以最小的代价添加或删除节点。因此,我们需要一个不直接依赖于节点(或服务器)数量的分发方案,以便在添加或删除节点时,需要重新定位的密钥数量最小化。

一致性哈希通过确保每次向上或向下扩展时,我们不必重新排列所有键或获悉所有服务器,从而解决了这个水平可扩展性问题。

既然我们理解了这个问题,让我们详细讨论一致哈希。

它是如何工作的

一致性哈希是一种分布式哈希方案,通过在抽象圆或哈希环上为节点分配位置,独立于分布式哈希表中的节点数进行操作。这允许在不影响整个系统的情况下扩展服务器和对象。

consistent-hashing.png

使用一致性哈希,只有 K/N 数据需要重新分发。

R=K/NR=K/N

者里

  • R: 需要重新分发的数据。

  • K: 分区键的数量。

  • N: 节点数。

哈希函数的输出是一个区间,比如 0...m-1 这些我们可以在哈希环上表示的数字。我们对请求进行哈希,并根据输出结果在环上分发它们。类似地,我们还对节点进行哈希,并将它们分布在同一个环上。

Hash(key1)=P1Hash(key2)=P2Hash(key3)=P3...Hash(keyn)=Pm1Hash(key_1)=P_1 \\ Hash(key_2)=P_2 \\ Hash(key_3)=P_3 \\ ... \\ Hash(key_n)=P_{m-1}

这里,

key:请求/节点ID或IP。

P: 哈希环上的位置。

m: 哈希环的总区间。

现在,当请求到来时,我们可以简单地以顺时针(也可以逆时针)的方式将其路由到最近的节点。这意味着,如果添加或删除新节点,我们可以使用最近的节点,只需要重新路由一小部分请求。

理论上,一致性哈希应该均匀地分配负载,但在实践中不是这样的。通常,负载分布是不均匀的,一台服务器可能处理大部分请求,成为热点,本质上是系统的瓶颈。我们可以通过添加额外节点来解决这个问题,但这可能会很昂贵。

让我们看看如何解决这些问题。

虚拟节点

为了确保负载分布更均匀,我们可以引入虚拟节点的概念,有时也称为VNode。

这里没有将单个位置分配给节点,而是将哈希区间划分为多个较小的区间,并为每个物理节点分配几个较小的区间。这些子区间中的每一个都被视为VNode。因此,虚拟节点基本上是跨哈希环多次映射的现有物理节点,以最小化对节点分配区间的更改。

virtual-nodes.png

为此,我们可以使用 k 个哈希函数。

Hash1(key1)=P1Hash2(key2)=P2Hash3(key3)=P3...Hashk(keyn)=Pm1Hash_1(key_1)=P_1 \\ Hash_2(key_2)=P_2 \\ Hash_3(key_3)=P_3 \\ ... \\ Hash_k(key_n)=P_{m-1}

这里,

密钥

key:请求/节点ID或IP。

k: 哈希函数的数量。

P: 哈希环上的位置。

m: 哈希环的总区间。

由于 vNode 通过将哈希区间划分为更小的子区间,有助于将负载更均匀地分布在集群上的物理节点上,因此可以加快添加或删除节点后的重新平衡过程。这也有助于我们减少出现热点的概率。

数据复制

为了确保高可用性和持久性,一致性哈希在系统中的多个 N 节点上复制每个数据项,其中值 N 等于复制因子。

复制因子是接收相同数据副本的节点数。在最终一致的系统中,这个过程一般异步完成。

优点

让我们看看一致性哈希的一些优点:

  • 使快速的向上或向下扩展更可预测。
  • 便于跨节点进行分区和复制。
  • 实现可扩展性和可用性。
  • 减少热点。

缺点

以下是一致性哈希的一些缺点:

  • 增加了复杂性。
  • 级联故障。
  • 负载分布仍然可能不均匀。
  • 当节点暂时故障时,密钥管理可能代价很高。

例子

让我们看一些使用一致性哈希的例子:

Apache Cassandra中的数据分区。

Amazon DynamoDB 中跨多个存储主机的负载分布。