一致性哈希
一致性哈希
当我们在做数据库分库分表或者是分布式缓存时,不可避免的都会遇到一个问题:如何将数据均匀的分散到各个节点中,并且尽量的在加减节点时能使受影响的数据最少。
最经典的方式就是index=hash(key)%N
这样来计算出需要存放的节点。其中
一致性
一致性哈希是一种特殊的哈希算法。在使用一致哈希算法后,哈希表槽位数(大小)的改变平均只需要对
环构建
一致
当节点发生故障下线,或者增加新的节点时候,依然根据顺时针方向,
虚拟节点
考虑到服务节点的个数以及
Java 中一致性哈希算法的实现
我们可以选择用某个有序数组或者
- 初始化一个长度为
N 的数组。 - 将服务节点通过
hash 算法得到的正整数,同时将节点自身的数据(hashcode、ip、端口等)存放在这里。 - 完成节点存放后将整个数组进行排序(排序算法有多种
) 。 - 客户端获取路由节点时,将自身进行
hash 也得到一个正整数; - 遍历这个数组直到找到一个数据大于等于当前客户端的
hash 值,就将当前节点作为该客户端所路由的节点。 - 如果没有发现比客户端大的数据就返回第一个节点(满足环的特性
) 。
只使用了
- 写入数据候,
TreeMap 可以保证key 的自然排序。 tailMap 可以获取比当前key 大的部分数据。- 当这个方法有数据返回时取第一个就是顺时针中的第一个节点了。
- 如果没有返回那就直接取整个
Map 的第一个节点,同样也实现了环形结构。
Go 中一致性哈希算法的实现与应用
func (m *Map) Add(nodes ...string) {
for _, n := range nodes {
for i := 0; i < m.replicas; i++ {
hash := int(m.hash([]byte(strconv.Itoa(i) + " " + n)))
m.nodes = append(m.nodes, hash)
m.hashMap[hash] = n
}
}
sort.Ints(m.nodes)
}
func (m *Map) Get(key string) string {
hash := int(m.hash([]byte(key)))
idx := sort.Search(len(m.keys),
func(i int) bool { return m.keys[i] >= hash }
)
if idx == len(m.keys) {
idx = 0
}
return m.hashMap[m.keys[idx]]
}