技术分享 | Jump Consistent Hash 原理解析(上篇)

技术分享 | Jump Consistent Hash 原理解析(上篇)作者:傅文辉 之前爱可生开源社区公众号发表了dble 沿用 jumpstringhash,移除 Mycat 一致性 hash 原因解析, 阐述了跳跃法相对环割法的性能优势。很多读者表示对其中”跳跃法…

技术分享 | Jump Consistent Hash 原理解析(上篇)

之前爱可生开源社区公众号发表了dble 沿用 jumpstringhash,移除 Mycat 一致性 hash 原因解析, 阐述了跳跃法相对环割法的性能优势。很多读者表示对其中”跳跃法的原理”不是很理解,本文就来详细阐述一下。

一致性哈希

首先,我们的需求是,将数据(key-value pair)分布在多个节点上。这点可以简单的用取模实现,

节点 key
1 1 4 7 10
2 2 5 8 11
3 3 6 9 12

然而,当增加新节点时,数据将发生大规模转移:

节点 key
1 (1) 5 9
2 (2) 6 10
3 (3) 7 11
4 4 8 12

一致性哈希的主要目的是,在节点数量发生变更时,只需要在节点间移动少量数据,而不是”全部洗牌”。

除了经典的环割法一致性哈希外,Google 发表了另一种实现简洁且高效的跳跃法一致性哈希《A Fast, Minimal Memory, Consistent Hash Algorithm》(文末附链接)

在爱可生开源数据库中间件 dble 中,关于 jump consistent hash 的配置方法详见 dble 官方手册中”跳增字符串算法”的部分(文末附链接)。

基础实现

与原始论文不同, 本文节点(又称 bucket)从 1 开始编号,而非从 0 开始。

  • 先考虑只有一个节点的情况,显然所有数据都放在这个节点里, 即 ch(key,1)=1 (ch 为 consistent_hash 之缩写)。
  • 考虑增加一个节点,我们随机抽取 1/2 的数据移动到 2 号节点
  • 考虑再增加一个节点,需要从 1、2 号节点中,随机抽取共 1/3 的数据移动到 3 号节点
    • 为了均匀分配, 1、2号需要各出 1/6 的数据
    • 实际上,只要每个 key 都有 1/3 的概率被抽中,分配总是均匀的

可以看到,每增加一个节点,只需要移动总共 1/n 的数据,而不是取模法中的几乎所有数据。

所谓随机抽取,我们采用可重现的随机:首次调用 Rand() 之前将 key 作为随机数种子。因而对于一个 key,首次放入和后续取回使用的是相同的随机数序列。

例如有 k1,k2,k3 三个 key, 随着节点数量从 1 到 15 增长, 它们各自会在某一时刻“跳跃”,而后“稳定”一段时间。

1 2 3 4 5 6 7 8 9 10 11 12 13 14
k1 1 1 3 3 5 5 5 5 5 5 5 5 5 5
k2 1 2 2 2 2 2 2 8 8 8 8 8 8 8
k3 1 2 2 2 2 6 6 8 8 8 11 11 11 11

我们用数学归纳法来表达一下某个 key 在不同节点数时的位置:

  • 基础情况:只有一个节点,只能放在节点 1
  • 归纳情况:假设目前有 n 个节点,增加一个节点到 n+1 个。key 目前所在的位置由之前的跳跃情况决定。本轮该 key 有 1/(n+1) 的概率被移到 n+1 号节点
    • 即 n+1 节点时,key 所在的位置由 n 节点时的位置和一个随机变量 rand 决定, 如果 rand<1/(n+1), 它就会跳跃到 n+1 节点, 否则则和 n 节点时一样

结合基础情况和归纳情况,我们得出了 n 为任意正整数时的分配方法。数学归纳法的逻辑和递归代码直接对应:

  func ch(r *rand.Rand, k int, i int) int {
  	if i == 1 {
      	// 基础情况
      	return 1
      } else {
      	// 归纳情况
      	b := ch(k, i-1)
          if rand.Float() < 1.0/float64(i) {
  	        return i
          } else {
  	    	return b
          }
      }
  }

func ch_wrapper(k int, i int) int {
    r := rand.Seed(k) // 在计算之前, 将key作为随机数种子
    return ch(r, k, i)
}

代码100分

注意,要先计算 ch(k, i-1) 再决定本轮是否跳转( if rand < 1.0/i )。不能因为本轮决定跳转就不计算上一轮的结果,否则会因节点数不同而产生不一样的随机序列。

工程代码中一般使用循环代替递归。本文不再赘述递归转循环的办法。

优化性能

我们看到,对于一个 key,我们要从 1~N(N 为节点数)循环一遍,即复杂度为节点数的线性关系. 原始论文中给出了一个巧妙的方法,使复杂度从线性降低到了对数:既然每一次是否跳跃的决策中我们随机决定,那么,与其一次次决定是否跳跃,我们是否能够直接随机地决定下一次跳跃的目标?当然,这个随机目标的取值符合一定的概率分布。

关于这个巧妙方法的具体内容和论证,敬请期待下篇。

文中相关资源链接: 《A Fast, Minimal Memory, Consistent Hash Algorithm》 https://arxiv.org/ftp/arxiv/papers/1406/1406.2294.pdf 《DBLE 手册中跳增字符算法部分》 https://actiontech.github.io/dble-docs-cn/1.config_file/1.01_rule.xml.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/9982.html

(0)
上一篇 2023-01-28 11:00
下一篇 2023-01-28 12:00

相关推荐

  • Python如何保存文件

    Python如何保存文件在我们的日常工作和生活中,保存文件是一个极其基础的功能,是我们非常常见的操作。Python语言作为一门强大的编程语言,同样也提供了保存文件的功能,帮助我们更好的将数据、信息等保存下来。那么,如何使用Python保存文件呢?接下来,我将为大家详细介绍。

    2024-09-15
    28
  • 52兔思网的web服务文件做好时刻与机器同步的几个步骤浅析[通俗易懂]

    52兔思网的web服务文件做好时刻与机器同步的几个步骤浅析[通俗易懂]在52兔思网www.52tusi.com里面,我们经常会在北京男士休闲会馆网里面搭建LAMP环境,并实践基于DNS做基于域名的虚拟主机中的环境,重新搭建一个同样的环境。 要求: a)实现web服务文…

    2023-03-03
    146
  • Oracle11以后的行列转换[通俗易懂]

    Oracle11以后的行列转换[通俗易懂]Oracle11以后,行列转换有了新的方法。 下面的是已经疏通过的代码,请放心使用。。。 With AA as ( Select A,B,C,row_number() over (partition

    2023-01-31
    141
  • 查看conda安装的包

    查看conda安装的包Conda是一个包管理系统和环境管理系统,可在计算机上管理多个语言的软件包及其依赖项。它旨在与Python语言一起使用,但也可以与R、Ruby、Lua等语言一起使用。本文将详细介绍如何查看conda安装的包,方便读者更好地管理自己的开发环境。

    2024-09-16
    22
  • 2017年下学期仁爱版英语教学工作计划[通俗易懂]

    2017年下学期仁爱版英语教学工作计划[通俗易懂]一、学期教学目标: 学生应有较明确的英语学习动机和积极主动的学习态度。能听懂教师对有关熟悉话题的陈述并能参与讨论。七年级学生能阅读的简单读物和报纸杂志,克服生词障碍,理解大意。能根据阅读目的运用适当…

    2022-12-25
    146
  • 【StoneDB研发日志】列式存储 delete方案调研

    【StoneDB研发日志】列式存储 delete方案调研MySQL删除数据的方式 以MySQL 5.7为例,数据库删除数据的方式一共有以下三种: delete truncate drop 以上三种方式都可以删除数据,但是使用场景是不同的。 对于整个表进行删

    2023-05-31
    150
  • PostgreSQL学习的九层宝塔「终于解决」

    PostgreSQL学习的九层宝塔「终于解决」武侠世界,9是个神奇的数字,武学秘籍有《九阳真经》《九阴真经》,凡武功修炼到第九层,闯荡江湖将独孤求败,快意恩仇。

    2023-04-05
    157
  • Python Tuple: 定义、索引和迭代不可变序列

    Python Tuple: 定义、索引和迭代不可变序列Python中,元组是一种不可变序列。元组使用圆括号表示,元素之间使用逗号分隔。元素可以是不同的数据类型,例如数字、字符串、列表等。元组的访问、索引、切片和迭代与列表类似,但是,元组的元素不能修改。这篇文章将详细介绍Python元组的定义、索引和迭代。

    2024-03-23
    71

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注