0
点赞
收藏
分享

微信扫一扫

一致性hash

以前一直听说过一致性hash,今天处理bug需要用到一致性hash,所以就看下:

keykey 的所有读写请求都必须一致地分配给同一个后端节点。 同时,分配的负载应该尽量均衡。

换句话讲, 我们需要寻找一种映射函数, 把随机到来的字符串 keykey ,一致地映射到 nn 个槽中。 此外,我们也希望,这个映射要做到尽量平均

 

Mod-N哈希的扩容问题 

下面,我们看下在前面提到的Mod-N哈希法的情况下, 新加一个节点或者移除一个故障节点会发生什么。

N4N4 时, 我们看到原本分配到 N2N2 的 kk 在扩容后会分配到 N1N1。 对于一个kvdb来说,这意味着我们需要在扩容后把 kk 从 N2N2 迁移到 N1N1 才能继续提供服务。 否则的话,扩容后的读请求将映射到新的节点 N1N1, 而导致读不到数据。

一致性hash_一致性哈希

新增一个节点,会导致新映射和老映射的不一致

 

一致性哈希算法 

我们希望构造一种函数 f(k,n)→mf(k,n)→m 把字符串映射到 nn 个槽上:

  • 它的输入是随机到来的字符串 kk 和 槽的个数 nn.
  • 输出是映射到的槽的标号 mm , m<nm<n.

这个函数需要有这样的性质:

  • 映射均匀: 对随机到来的输入 k, 函数返回任一个 m的概率都应该是 1/n 。
  • 一致性
  • 相同的 k, n 输入, 一定会有相同的输出。
  • 当槽的数目增减时, 映射结果和之前不一致的字符串的数量要尽量少。

一致性哈希算法 

我们希望构造一种函数 f(k,n)→mf(k,n)→m 把字符串映射到 nn 个槽上:

  • 它的输入是随机到来的字符串 kk 和 槽的个数 nn.
  • 输出是映射到的槽的标号 mm , m<nm<n.

这个函数需要有这样的性质:

  • 映射均匀: 对随机到来的输入 kk, 函数返回任一个 mm 的概率都应该是 1/n1/n 。
  • 一致性
  • 相同的 kk, nn 输入, 一定会有相同的输出。
  • 当槽的数目增减时, 映射结果和之前不一致的字符串的数量要尽量少。

这个算法的关键特征在于, 不要导致全局重新映射, 而是要做增量的重新映射

将介绍几种一致性哈希算法:

  • ​​哈希环法​​
  • ​​跳跃一致性哈希法​​

一致性哈希环算法 

具体的算法:

  1. 设 hash(key)hash(key) 是映射到区间 [0,232][0,232] 上的一个哈希函数。 把区间首尾相连,形成一个顺时针增长的哈希环(如图5.1) 。
  2. 将所有槽位(或者节点) N0,..,Nn−1N0,..,Nn−1 的标号 0,…,n−10,…,n−1 依次作为 hashhash 函数的输入进行哈希, 把结果分别标记在环上。
  3. 对于关于 kk 的映射,求出 z=hash(k)z=hash(k) , 标记在环上:
  1. 如果 zz 正好落在槽位上,返回这个槽位的标号。
  2. 否则, 顺时针沿着环寻找离 zz 最近的槽位,返回槽位标号

一致性hash_一致性哈希_02

 

 

哈希环做到了在槽位数量变化前后的增量式的重新映射, 避免了全量的重新映射。

假设整体的 kk 的数量是 KK , 由于哈希映射的均匀性, 所以,添加或者删除一个槽位,总会只影响一个槽位的映射量,也就是 1/K1/K , 因此,哈希环做到了最小化重新映射(minimum disruption),做到了​​完全的一致性​​

哈希环法的复杂度分析 

在技术实现上,实现哈希环的方法一般叫做 ketama 或 hash ring。 核心的逻辑在于如何在环上找一个和目标值 zz 相近的槽位, 我们把环拉开成一个自然数轴, 所有的槽位在环上的哈希值组成一个有序表。 在有序表里做查找, 这是​​二分查找​​可以解决的事情, 所以哈希环的映射函数的时间复杂度是 O(logn)O(logn)。

带权重的一致性哈希环 

实际应用中, 还可以对槽位(节点)添加权重。 大概的逻辑是构建很多指向真实节点的虚拟节点, 也叫影子节点。 影子节点之间是平权的,选中影子节点,就代表选中了背后的真实节点。 权重越大的节点,影子节点越多, 被选中的概率就越大。

下面的图6.2是一个例子, 其中 N0,N1,N2,N3的权重比是 1:2:3:2。 选中一个影子节点如 V(N2)就是选中了 N2 。

一致性hash_字符串_03

 

 

一致性哈希环下的热扩容和容灾 

回到​​kvdb的例子​​上来, 对于增删节点的情况,哈希环法做到了增量式的重新映射, 不再需要全量数据迁移的工作。 但仍然有部分数据出现了变更前后映射不一致, 技术运营上仍然存在如下问题:

  • 扩容:当增加节点时,新节点需要对齐下一节点的数据后才可以正常服务。
  • 缩容:当删除节点时,需要先把数据备份到下一节点才可以停服移除。
  • 故障:节点突然故障不得不移除时,面临数据丢失风险。

如果我们要实现动态扩容和缩容,即所谓的热扩容,不停止服务对系统进行增删节点, 可以这样做:

  1. 数据备份(双写): 数据写入到某个节点时,同时写一个备份(replica)到顺时针的邻居节点。
  2. 请求中继(代理): 新节点刚加入后,数据没有同步完成时,对读取不到的数据,可以把请求中继(replay)到顺时针方向的邻居节点。

结论:

实现了最小化的重新映射。

时间复杂度是 O(log(n))O(log(n)) , 空间复杂度是 O(n)O(n), 实际根据影子节点数量而乘上相应倍数。 映射均匀性不是很优秀。 热扩容和容灾的方式比较直观。

 

跳跃一致性哈希法

跳跃一致性哈希 ( Jump Consistent Hash ) 是 Google 于2014年发布的一个极简的、快速的一致性哈希算法​​[1]​​。 这个算法精简到可以用几行代码来描述, 下面的就是 Google 原论文中的算法的 C++ 表示:

int32_t JumpConsistentHash(uint64_t key, int32_t num_buckets) {
int64_t b = -1, j = 0;
while (j < num_buckets) {
b = j;
key = key * 2862933555777941757ULL + 1;
j = (b + 1) * (double(1LL << 31) / double((key >> 33) + 1));
}
return b;
}

函数 ​​JumpConsistentHash​​ 是一个一致性哈希函数, 它把一个 ​​key​​ 一致性地映射到给定几个槽位中的一个上, 输入 ​​key​​ 和槽位数量 ​​num_buckets​​, 输出映射到的槽位标号

参考:https://writings.sh/post/consistent-hashing-algorithms-part-3-jump-consistent-hash

 

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子



举报

相关推荐

0 条评论