GroupCache 一致性hash

Golang 的开源库GroupCache 传统hash的负载均衡 扩缩容 从hash(username) % 3⇒hash(username) % 4 负载均衡策略变化,任何用户都可能导致hash失效, 缓存集体失效可能导致后端直接承受过请求 一致性 Hash 的原理 有序 Hash 环的方式选择

Golang 的开源库GroupCache

Untitled.png

Untitled.png

传统hash的负载均衡

扩缩容 从hash(username) % 3hash(username) % 4 负载均衡策略变化,任何用户都可能导致hash失效, 缓存集体失效可能导致后端直接承受过请求

一致性 Hash 的原理

有序 Hash 环的方式选择目标缓存 Server 环中的每个节点对应于一台缓存 Server;每个节点也包含一个整数值。各节点按照该整数值从小到大依次排列

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

比如:新增一个节点 New-Node, 假设该节点的值为 11。那么新的有序 Hash 环如图

hash 值范围在 Node-B NewNode之间(即(7, 11])的数据缓存失效。这部分数据原本分配到节点 Node-C(值为 13),现在都需要迁移到新节点 NewNode上。

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

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

数据倾斜与虚拟节点

假设整个集群就两个缓存节点: Node-A Node-B。则 Node-B中将存放 Hash 值范围在 (Node-A, Node-B]之间的数据。而 Node-A将承担两部分的数据: hash < Node-A hash > Node-B,很容易出现分配给某个节点的数据远大于其他节点**“数据倾斜”**

Untitled.png

引入虚拟节点的概念,或者说是副本节点。每个真实的缓存 Server 在 Hash 环上都对应多个虚拟节点

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

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)
}

Untitled.png

LICENSED UNDER CC BY-NC-SA 4.0
Comment