一致性哈希算法golang版本

什么是一致性哈希

一致性哈希(Consistent Hashing)是一种分布式系统中常用的算法,用于在节点(如缓存服务器)之间均匀分配数据。它的核心思想是将所有可能的哈希值组织成一个环形结构,并将数据和节点通过哈希值映射到这个环上。这样在添加或删除节点时,只需重新分配极少量的数据,从而实现负载均衡和高可用性。

简单说:将节点均匀的分布,由一个环形结构,来将这些节点映射,实现负载均衡和高可用效果。

一致性hash原理

  1. 哈希环:将哈希值空间组织成一个环。
  2. 节点映射:将每个节点(如服务器)通过哈希函数映射到环上的某个点。
  3. 数据映射:将每个数据(如缓存对象)通过同样的哈希函数映射到环上的某个点。
  4. 数据存储:数据被存储到顺时针方向最近的节点中。
  5. 节点变化:当添加或删除节点时,只需重新分配很少的数据。例如,添加节点只会影响其顺时针方向的第一个节点之间的数据。

实现步骤

  1. 构建一个哈希环状结构
  2. 增删改查
go 复制代码
package main

import (
	"crypto/md5"
	"fmt"
	"hash"
	"sort"
	"strconv"
	"sync"
)
// 哈希环结构体
//包括副本数量(replicas)、哈希函数(hashFunc)、节点映射(nodes)、排序后的键(sortedKeys)和读写锁(mu)
type HashRing struct {
	replicas int
	hashFunc hash.Hash
	nodes    map[int]string
	sortedKeys []int
	mu       sync.RWMutex
}
// 创建一个新的HashRing实例,默认使用md5哈希函数。
func NewHashRing(replicas int, hashFunc hash.Hash) *HashRing {
	if hashFunc == nil {
		hashFunc = md5.New()
	}
	return &HashRing{
		replicas:   replicas,
		hashFunc:   hashFunc,
		nodes:      make(map[int]string),
		sortedKeys: []int{},
	}
}
// 添加节点,并为每个节点生成replicas个副本,将它们添加到哈希环中。
func (h *HashRing) AddNode(node string) {
	h.mu.Lock()
	defer h.mu.Unlock()

	for i := 0; i < h.replicas; i++ {
		hashKey := h.hashKey(fmt.Sprintf("%s%d", node, i))
		h.nodes[hashKey] = node
		h.sortedKeys = append(h.sortedKeys, hashKey)
	}

	sort.Ints(h.sortedKeys)
}
//删除节点,并从哈希环中移除该节点的所有副本。
func (h *HashRing) RemoveNode(node string) {
	h.mu.Lock()
	defer h.mu.Unlock()

	for i := 0; i < h.replicas; i++ {
		hashKey := h.hashKey(fmt.Sprintf("%s%d", node, i))
		delete(h.nodes, hashKey)
		h.removeSortedKey(hashKey)
	}
}
//根据键找到对应的节点,通过哈希键在排序后的键数组中查找合适的位置。
func (h *HashRing) GetNode(key string) string {
	h.mu.RLock()
	defer h.mu.RUnlock()

	if len(h.nodes) == 0 {
		return ""
	}

	hashKey := h.hashKey(key)
	idx := h.searchKey(hashKey)

	return h.nodes[h.sortedKeys[idx]]
}
// 计算字符串的哈希值。
func (h *HashRing) hashKey(key string) int {
	h.hashFunc.Reset()
	h.hashFunc.Write([]byte(key))
	hashBytes := h.hashFunc.Sum(nil)
	hashInt, _ := strconv.Atoi(fmt.Sprintf("%x", hashBytes)[:8])
	return hashInt
}
// 在排序后的键数组中查找哈希键的位置。
func (h *HashRing) searchKey(hashKey int) int {
	idx := sort.Search(len(h.sortedKeys), func(i int) bool {
		return h.sortedKeys[i] >= hashKey
	})

	if idx == len(h.sortedKeys) {
		return 0
	}

	return idx
}
// 从排序后的键数组中移除指定的键。
func (h *HashRing) removeSortedKey(key int) {
	idx := h.searchKey(key)
	h.sortedKeys = append(h.sortedKeys[:idx], h.sortedKeys[idx+1:]...)
}
// 测试代码
func main() {
	hashRing := NewHashRing(3, nil)

	hashRing.AddNode("node1")
	hashRing.AddNode("node2")
	hashRing.AddNode("node3")

	fmt.Println("Node for key 'my-key-1':", hashRing.GetNode("my-key-1"))
	fmt.Println("Node for key 'my-key-2':", hashRing.GetNode("my-key-2"))
	fmt.Println("Node for key 'my-key-3':", hashRing.GetNode("my-key-3"))

	hashRing.RemoveNode("node2")

	fmt.Println("Node for key 'my-key-1' after removing node2:", hashRing.GetNode("my-key-1"))
	fmt.Println("Node for key 'my-key-2' after removing node2:", hashRing.GetNode("my-key-2"))
	fmt.Println("Node for key 'my-key-3' after removing node2:", hashRing.GetNode("my-key-3"))
}
相关推荐
pin️‍灼灼灼灼18 分钟前
Linux——线程练习
linux·服务器·算法
摸鱼的快乐你不懂18 分钟前
Leetcode[反转链表]
算法·leetcode·链表
唐果然19 分钟前
SAR目标检测
算法·目标检测·目标跟踪
IT数据小能手40 分钟前
使用Go语言实现高效的数据挖掘
开发语言·golang·数据挖掘
Ai实战1 小时前
上帝之眼(BEVSee):多相机间无需标定,将各自目标统一到同一坐标系下(代码开源,提供数据集)
算法
灭霸11231 小时前
力扣 用队列实现栈(Java)
算法·leetcode·职场和发展
-代号95272 小时前
【LeetCode】十、二分查找法:寻找峰值 + 二维矩阵的搜索
算法·leetcode·矩阵
@干吧jyb2 小时前
贪心算法练习题(7/2)
数据结构·算法·leetcode·贪心算法
A22743 小时前
LeetCode 1327, 383, 236
java·算法·leetcode
jiayoushijie-泽宣3 小时前
深入浅出3D感知中的优化与基于学习的技术 (第三章) 原创教程
人工智能·算法·机器学习·3d·机器人