一致性 Hash 原理及 GroupCache 源码分析

相比于直接对 hash 取模得到目标 Server 的做法,一致性 Hash 采用 有序 Hash 环 的方式选择目标缓存 Server。如下图所示:

一致性 Hash 原理及 GroupCache 源码分析

对于该有序 Hash 环,环中的每个节点对应于一台缓存 Server,同时每个节点也包含一个整数值。各节点按照该整数值从小到大依次排列。

对于指定用户来说,我们依然首先出计算用户名的 hash 值。接着,在 Hash 环中找到 第一个大于等于该 hash 值的节点 ,将其作为目标缓存 Server。

例如,我们 hash 环中的三个节点 Node-A 、 Node-B 、 Node-C 的值依次为 3、7、13。假设对于某个用户来说,我们计算得到其用户名的 hash 值为 9,环中第一个大于 9 的节点为 Node-C,则选用 Node-C 作为该用户的缓存 Server。

缓存失效的缓解

===========

以上就是正常情况下一致性 Hash 的使用,接下来我们看下,一致性 Hash 是如何应对集群的扩缩容的。

当我们对集群进行扩容,新增一个节点 New-Node , 假设该节点的值为 11。那么新的有序 Hash 还如下图所示:

一致性 Hash 原理及 GroupCache 源码分析

我们看下此时的缓存失效情况:在这种情况下, 只会造成 hash 值范围在 Node-B 和 NewNode之间(即(7, 11])的数据缓存失效。这部分数据原本分配到节点 Node-C (值为 13),现在都需要迁移到新节点 NewNode 上。

而原本分配到 Node-A 、 Node-B 两个节点上的缓存数据,不会受到任何影响。之前值范围在 NewNode 和 Node-B 之间(即(11, 13])的数据,被分配到了 Node-C 上面。新节点出现后,这部分数据依然属于 Node-C ,也不会受到任何影响。

一致性 Hash 利用有序 Hash 环,巧妙的缓解了集群扩缩容造成的缓存失效问题。注意,这里说的是 “缓解”,缓存失效问题无法完全避免,但是可以将其影响降到最低。

这里有个小问题是,因为有序 Hash 还需要其中每个节点有持有一个整数值,那这个整数值如何得到呢?一般做法是,我们可以利用该节点的特有信息计算其 Hash 值得到, 例如 hash(ip:port) 。

数据倾斜与虚拟节点

=============

以上介绍了一致性 hash 的基本过程,这么看来,一致性 hash 作为缓解缓存失效的手段,的确是行之有效的。

但我们考虑一个极限情况,假设整个集群就两个缓存节点: Node-A 和 Node-B 。则 Node-B 中将存放 Hash 值范围在 (Node-A, Node-B] 之间的数据。而 Node-A 将承担两部分的数据: hash < Node-A 和 hash > Node-B 。

从这个值范围,我们可以轻易的看出, Node-A 的值空间实际上远大于 Node-B 。当数据量较大时, Node-A 承担的数据也将远超于 Node-B 。实际上,当节点过少时,很容易出现分配给某个节点的数据远大于其他节点。这种现象我们往往称之为 “数据倾斜”。

对于此类问题,我们可以引入虚拟节点的概念,或者说是副本节点。每个真实的缓存 Server 在 Hash 环上都对应多个虚拟节点。如下图所示:

一致性 Hash 原理及 GroupCache 源码分析

对于上图来说,我们其实依然只有三个缓存 Server。但是每个 Server 都有一个副本,例如 V-Node-A 和 Node-A 都对应同一个缓存 Server。

GroupCache 的一致性 Hash 实现

===========================

GroupCache 提供了一个简单的一致性 hash 的实现。其代码在 github.com/golang/groupcache/consistenthash 。

我们先看下它的使用方法:

import (

“fmt”

“github.com/golang/groupcache/consistenthash”

)

func main() {

// 构造一个 consistenthash 对象,每个节点在 Hash 环上都一共有三个虚拟节点。

hash := consistenthash.New(3, nil)

// 添加节点

hash.Add(

“127.0.0.1:8080”,

“127.0.0.1:8081”,

“127.0.0.1:8082”,

)

// 根据 key 获取其对应的节点

node := hash.Get(“cyhone.com”)

fmt.Println(node)

}

consistenthash 对外提供了三个函数:

  1. New(replicas int, fn Hash) :构造一个 consistenthash 对象, replicas 代表每个节点的虚拟节点个数,例如 replicas 等于 3,代表每个节点在 Hash 环上都对应有三个虚拟节点。 fn 代表自定义的 hash 函数,传 nil 则将会使用默认的 hash 函数。

  2. Add 函数:向 Hash 环上添加节点。

  3. Get 函数:传入一个 key,得到其被分配到的节点。

Add 函数

==========

我们先看下其 Add 函数的实现。Add 函数用于向 Hash 环上添加节点。其源码如下:

func (m *Map) Add(keys …string) {

for _, key := range keys {

for i := 0; i < m.replicas; i++ {

hash := int(m.hash([]byte(strconv.Itoa(i) + key)))

m.keys = append(m.keys, hash)

m.hashMap[hash] = key

}

}

// 排序,这个动作非常重要,因为只有这样,才能构造一个有序的 Hash 环

sort.Ints(m.keys)

}

在 Add 函数里面涉及两个重要的属性:

  1. keys: 类型为 []int 。这个其实就是我们上面说的有序 Hash 环,这里用了一个数组表示。数组中的每一项都代表一个虚拟节点以及它的值。

  2. hashMap:类型为 map[int]string 。这个就是虚拟节点到用户传的真实节点的映射。map 的 key 就是 keys 属性的元素。

在这个函数里面有生成虚拟节点的操作。例如用户传了真实节点为 [“Node-A”, “Node-B”] , 同时 replicas 等于 2。则 Node-A 会对应 Hash 环上两个虚拟节点: 0Node-A , 1Node-A ,这两个节点对应的值也是直接进行对其计算 hash 得到。

需要注意的是,每次 Add 时候,函数最后会对 keys 进行排序。因此最好一次把所有的节点都加进来,以避免多次排序。

Get 函数

==========

接下来我们分析下 Get 函数的使用,Get 函数用于被指定 key 分配对应节点。其源码如下:

func (m *Map) Get(key string) string {

if m.IsEmpty() {

return “”

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

看完上述知识点如果你深感Java基础不够扎实,或者刷题刷的不够、知识不全面

小编专门为你量身定制了一套<Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法>

image

针对知识面不够,也莫慌!还有一整套的<Java核心进阶手册>,可以瞬间查漏补缺

image

全都是一丢一丢的收集整理纯手打出来的

更有纯手绘的各大知识体系大纲,可供梳理:Java筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

image

image
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
筑基、MySQL、Redis、并发编程、Spring、分布式高性能架构知识、微服务架构知识、开源框架知识点等等的xmind手绘图~

[外链图片转存中…(img-vdrOPfw2-1713567691886)]

[外链图片转存中…(img-cToJq9za-1713567691886)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值