一致性 hash 算法是在分布式应用中使用广泛。其主要作用是为了解决服务中的热点问题。例如在分布式数据存储,比如Redis缓存集群、有状态的任务作业等,通过其解决请求的热点问题,并且可以缓解当发生服务变动时出现的负载不平衡情况。

本文将简单介绍一致性HASH算法,并分析golang的一个开源实现包(github.com/stathat/consistent)

一致性hash算法原理

解决的问题

在分布式存储中,为了保证不同机器缓存的数据均衡性以及负载的均衡性,一般会在代理层先做一次hash。正常情况下,我们会做取模操作,然后将服务分配给不同的机器。但是这种情况下当某台服务宕机或者有新机器加入时,会直接影响到所有机器的缓存。为了解决这个问题,所以出现了一致性hash算法。

如何解决

一致性hash算法,主要是解决如何将hash后的值映射到既定的机器中。下面是hash步骤:

1,将hash值构造为一个圆环,一般为 [0,2^32 - 1] 2. 在圆环上均匀的挑选hash点,作为机器的节点 3. 在访问机器时,hash到圆环的某个点上,然后顺时针访问到的第一个节点即为需要访问的机器。

这样,当某台机器宕机时,宕机机器的下一台机器将承包宕机机器的存储任务。如果hash环中的两个机器节点中添加了一个机器节点,那原有的节点存储任务将被分一部分存储任务给新增加的节点。

升华

正常情况下,如果使用上面说的一致性hash算法,出现宕机时,一台机器将干两台机器的活,这显然是不科学的。因此还需要有虚拟节点的概念。将虚拟节点放在圆环上,然后真正的服务节点维护多个虚拟节点(并且需要保证一台服务节点上的多个虚拟节点需要离散分布)。这样,当一个服务节点宕机后,其实是多个离散的虚拟节点从hash环中消失,这样可以保证正常的机器在保留原有数据的基础上,还能承载宕机对应的离散节点的负载。

一致性hash算法的golang实现

github.com/stathat/consistent 是golang实现的一个一致性hash算法。

数据结构

在实现中,增加了分布式缓存中较为实用的副本的概念。结构如下:

type Consistent struct {circle           map[uint32]string  // 保存环members          map[string]bool  // 成员标记sortedHashes     uints // []int32 类型  标记了环上的虚拟节点,有序的sliceNumberOfReplicas int // 副本数量count            int64 // 节点数量scratch          [64]byte // 未使用UseFnv           bool // 标记使用 FNV-1a hashsync.RWMutex  // 读写锁标记
}

设置节点

初始化节点时,需要提供各个节点的名称,以及副本的数量。初始化将进行如下操作:

  1. 将hash与node名的映射存入circle中

  2. 将members 标记为true

  3. 将hash值放入有序的sortedHashes中

func (c *Consistent) Set(elts []string) {c.Lock()defer c.Unlock()for k := range c.members {  // 首先将清除不在elts 集合中的数据found := falsefor _, v := range elts {if k == v {found = truebreak}}if !found {c.remove(k)}}for _, v := range elts {  // 之后依次添加_, exists := c.members[v]if exists {continue}c.add(v)}
}
func (c *Consistent) add(elt string) {for i := 0; i < c.NumberOfReplicas; i++ {c.circle[c.hashKey(c.eltKey(elt, i))] = elt}c.members[elt] = truec.updateSortedHashes()  // 更新sortedHashes 字段,保证有序c.count++
}

访问

当通过name映射一个node时,首先计算hash值,然后通过二分查找的方法从sortedHashes slice中拿到对应的node的hash值,最后通过circle映射出对应的node name.

func (c *Consistent) Get(name string) (string, error) {c.RLock()defer c.RUnlock()if len(c.circle) == 0 {return "", ErrEmptyCircle}key := c.hashKey(name)i := c.search(key)return c.circle[c.sortedHashes[i]], nil
}

删除

当某台机器宕机时,可以删除对应的node 节点,以释放快速调整服务请求。

func (c *Consistent) remove(elt string) {// 代码中有加锁for i := 0; i < c.NumberOfReplicas; i++ {delete(c.circle, c.hashKey(c.eltKey(elt, i)))}delete(c.members, elt)c.updateSortedHashes()c.count--
}

总结

从代码的实现中,我们可以看出:

  • 从环中查找下一个节点,只需要将所有节点的hash值存放在一个有序的slice中,做二分查找即可。为了保证每个节点数据的均衡性,一般会添加较多的虚拟节点,为了保证访问请求的速度,又不能过多。例如,在Codis中,虚拟节点默认一般有1024个(当然也可以配置更多的槽)

  • 一致性hash对服务在做调整时可以做平滑过度

  • 比较常见的应用,例如分布式缓存服务 Redis-Cluster, Codis;或者 服务代理 twemproxy 等

获取最新的文章,欢迎关注我的公众号

Golang 一致性Hash算法实现相关推荐

  1. 一致性 Hash 算法原理总结

    一致性 Hash 算法是解决分布式缓存等问题的一种算法,本文介绍了一致性 Hash 算法的原理,并给出了一种实现和实际运用的案例: 一致性 Hash 算法背景 考虑这么一种场景: 我们有三台缓存服务器 ...

  2. 一致性 Hash 算法的实际应用

    前言 记得一年前分享过一篇<一致性 Hash 算法分析>,当时只是分析了这个算法的实现原理.解决了什么问题等. 但没有实际实现一个这样的算法,毕竟要加深印象还得自己撸一遍,于是本次就当前的 ...

  3. 不会一致性hash算法,劝你简历别写搞过负载均衡

    这两天看到技术群里,有小伙伴在讨论一致性hash算法的问题,正愁没啥写的题目就来了,那就简单介绍下它的原理.下边我们以分布式缓存中经典场景举例,面试中也是经常提及的一些话题,看看什么是一致性hash算 ...

  4. 什么是一致性 Hash 算法

    数据分片 先让我们看一个例子吧 我们经常会用 Redis 做缓存,把一些数据放在上面,以减少数据的压力. 当数据量少,访问压力不大的时候,通常一台Redis就能搞定,为了高可用,弄个主从也就足够了: ...

  5. hash算法_一致性hash算法简介

    一致性hash算法有什么用?我们为什么需要一致性hash算法?这两个问题的答案可以看这篇文章 分布式系统路由算法简介. 了解了一致性hash算法出现的背景,我们来看看什么是一致性hash算法.一致性h ...

  6. 一致性Hash算法(KetamaHash)的c#实现

    Consistent Hashing最大限度地抑制了hash键的重新分布.另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上.因为使用一般的 ...

  7. 一致性hash算法_分布式寻址算法

    一.分布式寻址算法简介 分布式寻址算法是很重要的内容,不了解这些算法,也就不能透彻的了解各种分布式中间件的原理.简单说一下这些高大上的寻址到底是个啥意思,比如在elasticsearch中,采用的是多 ...

  8. Java算法之 一致性hash算法原理及实现

    为什么80%的码农都做不了架构师?>>>    一致性hash算法原理及实现 转载于:https://my.oschina.net/90888/blog/1645131

  9. php 实现一致性hash 算法 memcache

    散列表的应用 涉及到数据查找比对,首先考虑到使用HashSet.HashSet最大的好处就是实现查找时间复杂度为O(1).使用HashSet需要解决一个重要问题:冲突问题.对比研究了网上一些字符串哈希 ...

最新文章

  1. 浙江大学通信与计算机网络,新浙大2015年通信与计算机网络离线作业.doc
  2. php删除指定图片吗,PHP 去除图片指定颜色
  3. “Could not import PIL.Image. The use of array_to_img requires PIL.”错误的解决办法
  4. java 属性自定义配置,将自定义FXML属性设置为自定义javafx组件的参数
  5. Lesson 5.基本优化思想与最小二乘法
  6. 经验:在mysql中避免重复插入数据的4种方式
  7. AGPM(高级组策略管理)3.0之二操作
  8. 2015-05-31
  9. 训练日志 2019.1.19
  10. SpringCloud工作笔记038---spring cloud-简单网关权限控制_直接在zuul里面做
  11. 程序员摆脱疲劳的 11 个建议
  12. 贺利坚老师汇编课程56笔记:CMP指令
  13. 让解析器可以快速处理词法单元之间的空格
  14. 阿里云服务器如何升级配置和降低配置?
  15. DispatcherServlet和Spring应用上下文初始化详解
  16. mysql生成随机姓名、手机号、日期
  17. 分享几个做CTF的网站
  18. 操作系统进程同步作业题
  19. python和java可以一起学吗_python可以和java一起学吗
  20. ubuntu Anaconda安装gi模块 ValueError: Namespace Gtk not available

热门文章

  1. 网页被重新刷新(网址后面自动添加了?)
  2. 镍氢电池怎么修复充电
  3. 搜集博客园邀月工作室的有关加密对称加密的文章
  4. infinity mysql,SQLException: 'Infinity' is not a valid numeric or
  5. java 字符串逆序
  6. 简化Kubernetes应用部署工具-Helm简介
  7. Java全栈(二)JavaSE:22.泛型
  8. 电压互感器TV1013-1H电路解析
  9. flutter 初体验 页面的跳转
  10. 免费第三方天气数据调用接口