草庐IT

一致性哈希的简单认识

程序员翔仔 2023-03-28 原文

简介

在分布式集群中,对机器的添加、删除或者是机器故障后自动脱离集群等操作是分布式集群管理最基本的功能。如果采用的是常见的取模哈希算法,当有机器添加、删除之后,需要对数据做迁移,非常麻烦。

而一致性哈希利用哈希环的概念,保证增加或减少服务器,数据存储的改变最少,相比取模哈希算法大大节省了数据移动的开销,非常方便。

一致性哈希认为在动态变化的缓存空间环境中,良好的哈希算法应该满足以下几个方面:

  • 平衡性:指哈希的结果能够尽可能分布到所有的缓存中,这样可以使得所有的缓存空间都能得到利用
  • 单调性:指当新的缓存空间加入时,原本已分配的数据可以被映射到原本或者新的缓存空间中,而不会被映射到旧的其他缓存空间中
  • 分散性:避免出现相同的内容被不同的终端映射到不同的缓存空间中,降低系统存储的效率
  • 负载:与分散性结合理解,对于一个特定的缓冲区,避免被不同的终端映射为不同的内容

一致性哈希可以理解成普通取模哈希算法的改良版,改变的是将普通的线性哈希空间变成环状的哈希空间,其中每一个缓存空间是环上的一个节点,数据一般存储在沿顺时针的方向找到的环上的第一个节点。

算法

哈希环

简单的说,一致性哈希是将整个哈希值空间想象成一个虚拟的圆环。

比如,假设哈希函数 H 的值空间为 0 ~ \(2^{32}-1\),整个哈希空间环如下:

假设这时有 k1、k2、k3、k4 这几个 key 值,通过一定的哈希算法,将这几个 key 值被平均分配到哈希环上。

同样的,现在有 3 台 cache 服务器,通过一定的哈希算法,也被平均分配到哈希环上。

下图展示的是 key 值被分配到哪一台 cache 服务器的一个示例:

如上所示:k1 存储在 c3 上,k4、k3 存储在 c1 上,k2 存储在 c2 上。

哈希环会将哈希后的 key 值按照顺时针的方向寻找最近的 cache 服务器,然后将数据存储在这台服务器上。

删除节点

假设 c3 服务器宕机,这时候需要从集群中将其摘除,其上的数据也需要做迁移。

按照一致性哈希的规则,原本存储在 c3 上的 k1 按照顺时针的方向寻找最近的 cache 服务器,即后续 k1 会存储在 c1 上:

从这里可知,当使用一致性哈希时,删除节点 c3 会影响到被删除节点 c3 上及其下一个节点 c1 的数据,迁移数据的时候,需要将被删除节点 c3 上的数据迁移到其下一个节点 c1 上。

新增节点

假设现在需要新增 c4 服务器,会破坏现在集群的平衡,需要对数据做一些处理。

假设 c4 服务器定位在 k4 和 k3 之间,按照一致性哈希的规则,原本存储在 c1 上的 k4 会迁移到 c4 上:

同样的,新增节点会影响到新增节点所在位置的后一个节点 c1,迁移数据的时候,需要将新增节点所在位置到其上一个节点 c3 之间的数据从其下一个节点 c1 迁移到新增的节点 c4 上。

虚拟节点

一致性哈希理论上没有什么问题,但实际使用会存在以下问题:

  • 当节点较少时,哈希环中的节点容易出现分布不均衡,最终导致数据倾斜
  • 当一个节点宕机时,数据会立马迁移到下一个节点,下一个节点的流量压力和内存压力都会增大,可能会导致其宕机,继而引发雪崩

这里就衍生出一个 虚拟节点 的概念,即对每个物理节点计算多个哈希值,将原来单一的物理节点在哈希环上虚拟出几个分身节点,这些分身节点称为虚拟节点。

映射到分身节点上的数据实际上就是映射到分身对应的物理节点上,这样一个物理节点可以通过虚拟节点的方式均匀分散在哈希环的各个部分,解决数据倾斜问题。

由于虚拟节点分散在哈希环各个部分,当某个节点宕机下线,虚拟节点所存储的数据会被均匀分配给下一个虚拟节点,则物理节点也会得到均匀分配,避免了对单一节点突发压力导致的节点雪崩问题。

在实际应用中,通常将虚拟节点数设置成 32 甚至更大,这样可以保证即使很少的服务节点也能做到均匀的数据分布。

优缺点

一致性哈希算法相比普通的哈希算法在扩展性和容错性上都有一定的优势:

  • 扩展性:普通的哈希算法增加缓存空间的时候,需要对大量数据做迁移;一致性哈希算法扩展时仅需将下一个节点中的一部分数据迁移到这个新增节点上
  • 容错性:普通的哈希算法减少缓存空间的时候,会出现哈希映射大面积失效的情况;而对于一致性哈希算法,如果出现需要减少缓存空间的情况,其实就是需要将当前减少的节点数据迁移到下一个节点中

实际上,不会存在一劳永逸的哈希算法,一致性哈希算法在以下场景需要谨慎使用:

  • 对于数据占用空间大、但数量较小时,使用一致性哈希有些大材小用
  • 一致性哈希解决了数据的均匀分布,但是没有解决流量和负载的均衡
  • 数据和机器之间的映射通过哈希算法得到,这种关系比较固定,无法人工干涉

有关一致性哈希的简单认识的更多相关文章

  1. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  2. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  3. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  4. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  5. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  6. ruby - 在哈希的键数组中追加元素 - 2

    查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用

  7. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  8. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  9. Qt Designer的简单使用 - 2

    在前面两节的例子中,主界面窗口的尺寸和标签控件显示的矩形区域等,都是用C++代码编写的。窗口和控件的尺寸都是预估的,控件如果多起来,那就不好估计每个控件合适的位置和大小了。用C++代码编写图形界面的问题就是不直观,因此Qt项目开发了专门的可视化图形界面编辑器——QtDesigner(Qt设计师)。通过QtDesigner就可以很方便地创建图形界面文件*.ui,然后将ui文件应用到源代码里面,做到“所见即所得”,大大方便了图形界面的设计。本节就演示一下QtDesigner的简单使用,学习拖拽控件和设置控件属性,并将ui文件应用到Qt程序代码里。使用QtDesigner设计界面在开始菜单中找到「Q

  10. ruby - 在 Ruby 中创建按公共(public)键值分组的新哈希 - 2

    假设我有一个在Ruby中看起来像这样的哈希:{:ie0=>"Hi",:ex0=>"Hey",:eg0=>"Howdy",:ie1=>"Hello",:ex1=>"Greetings",:eg1=>"Goodday"}有什么好的方法可以将它变成如下内容:{"0"=>{"ie"=>"Hi","ex"=>"Hey","eg"=>"Howdy"},"1"=>{"ie"=>"Hello","ex"=>"Greetings","eg"=>"Goodday"}} 最佳答案 您要求一个好的方法来做到这一点,所以答案是:一种您或同事可以在六个月后理解

随机推荐