一致性哈希算法的golang实现

1. 一致性哈希算法

关于一致性哈希算法的详解,可以参见这篇博客文章 一致性哈希详解

简单来说,一致性哈希通过哈希环和增加虚拟节点来解决节点变更时的大范围数据迁移和冷热不均的问题

一致性哈希的一个开源实现为:stathat.com/c/consistent

 

1.1 数据迁移

我们首先从数据迁移的角度来讨论哈希算法和一致性哈希算法,假设我们当前有3个节点,要新增一个节点变为4个节点

对于哈希算法来说,一个key是大概率发生迁移的,因为key%3不太可能等于key%4,并且迁移的节点涉及到集群所有的节点,很可能因为数据迁移导致整个集群不可用

而对于一致性哈希算法来讲,当新增节点时,数据迁移只会发生在新增节点和它顺时针的后一个节点之间,所涉及的数据也仅仅是新增节点和它逆时针的前一个节点之间的数据

在数据迁移方面,一致性哈希保证了少数数据迁移和少数节点参与数据迁移,极大地提升了系统的稳定性

 

下面反映的是使用哈希算法,在新增节点的情况下,数据迁移率的变化

可以很明显的看到,当集群规模变大时,新增/删除一个节点对系统带来的影响将是致命的,90%以上的数据都将被迁移

$ go run ./hash.go -keys 10000000 -nodes 3 -new-nodes 4
74.999980%

$ go run ./hash.go -keys 10000000 -nodes 10 -new-nodes 11
90.909000%

$ go run ./hash.go -keys 10000000 -nodes 20 -new-nodes 21 
95.238000%

 

下面反映的是使用一致性哈希,在新增节点的情况下,数据迁移率的变化

可以很明显的看到,随着集群规模的变大,新增/删除一个节点对系统带来的影响越小,迁移的数据量将会越少

$ go run ./consistent-hash.go -keys 10000000 -nodes 3 -new-nodes 4
24.301550%

$ go run ./consistent-hash.go -keys 10000000 -nodes 10 -new-nodes 11
6.479330%

$ go run ./consistent-hash.go -keys 10000000 -nodes 20 -new-nodes 21
4.928480%

 

1.2 冷热不均

什么是冷热不均问题呢?在一致性哈希中,我们将节点通过一定的哈希规则映射为哈希环上的一系列点

因为我们的数据读写规则是一个key在哈希环上的映射,沿着顺时针遇到的第一个节点就是该key的存储节点

设想这样一个场景:哈希环上所有的节点映射都集中在一片区域,这就导致了第一个节点将承受巨大的访问压力,这就是节点间的冷热不均现象

如何解决冷热不均?引入虚拟节点,将一个存储实例映射为多个虚拟节点,分布在哈希环上,借此解决冷热不均问题

 

在下面的模拟中我们可以看到普通的一致性哈希和带虚拟节点的一致性哈希算法的节点访问比例

很明显,我们可以看到带虚拟节点的一致性哈希平衡了各个节点之间的访问比例,解决了冷热不均问题

$ go run ./consistent-hash-vnode.go -keys 10000000 -nodes 3 -vnode 100
normal mode: node0 79.677728% , node1 87.908229% , node2 132.414073%
vnode mode: node0 102.326440% , node1 95.905930% , node2 101.767660%

$ go run ./consistent-hash-vnode.go -keys 10000000 -nodes 3 -vnode 200
normal mode: node0 79.677728% , node1 87.908229% , node2 132.414073%
vnode mode: node0 100.630600% , node1 107.318141% , node2 92.051289%

 

2. golang实现一致性哈希

最后我们简单看一下文中使用的一致性哈希开源实现stathat.com/c/consistent

其核心方法就是如下几个

// 初始化一致性对象
cons := consistent.New()

// 新增节点
cons.Add("cacheA")

// 删除节点
cons.Remove("cacheB")

// 获取数据存储的节点信息
server, err := cons.Get("user_89138238")

 

非常简单的实现,利用哈希和节点之间的映射就可以做到

 

参考:

https://github.com/stathat/consistent/blob/master/consistent.go

https://time.geekbang.org/column/article/207426

posted @ 2022-05-18 16:44  aganippe  阅读(296)  评论(0编辑  收藏  举报