【go项目-geecache】动手写分布式缓存 - day4 - 一致性哈希(hash)

索引

【go项目-geecache】动手写分布式缓存 - day1 - 实现LRU算法
【go项目-geecache】动手写分布式缓存 - day2 - 单机并发缓存
【go项目-geecache】动手写分布式缓存 - day3 - HTTP 服务端
【go项目-geecache】动手写分布式缓存 - day4 - 一致性哈希(hash)
【go项目-geecache】动手写分布式缓存 - day5 - 分布式节点
【go项目-geecache】动手写分布式缓存 - day6 - 防止缓存击穿
【go项目-geecache】动手写分布式缓存 - day7 - 使用 Protobuf 通信

收获

  1. 学会了什么是一致性哈希 ; 解决分布式负载问题
  2. 学会如何实现一致性哈希

一致性哈希是什么?

一致性哈希(Consistent Hashing)是一种常用的哈希算法,主要用于解决分布式系统中的负载均衡问题。它的主要思想是将数据和节点映射到一个虚拟环形空间中,通过一致的哈希函数将数据均匀地分布在环上,然后根据节点在环上的位置将数据映射到相应的节点上,从而实现负载均衡。

一致性哈希具体实现思想 :

-首先把整个哈希值空间变成一个环,然后把key哈希放在环上,接着把value哈希放在环上,我们让value哈希的位置的顺时针的下一个key哈希作为对应关系 ,因为整个哈希空间是个环,这样即使某些节点增加或者减少,也只需要修改节点附近的对应关系

数据分布不均 :采用虚拟节点

可能有些节点会对应大多数的数据,这样分布不均,我们添加为每个节点添加几个虚拟节点,这样每个虚拟节点分布比较均匀,最后在把虚拟节点映射回真实节点

分布式哈希实现

结构体实现
package consistenthash  
import (  
	"hash/crc32"  
	"sort"  
	"strconv"  
)  
type Hash func(data []byte) uint32  
type Map struct {  
	hash     Hash  
	replicas int  
	keys     []int // Sorted  
	hashMap  map[int]string  
}
  • hash:表示哈希函数,为一个 Hash 类型的函数变量。
  • replicas:表示每个键值对在哈希表中的副本数量。
  • keys:表示哈希表中所有键的有序数组。
  • hashMap:表示哈希表,为一个从 int 类型的键到 string 类型的值的映射。
实现实例化函数
func New(replicas int, fn Hash) *Map {  
	m := &Map{  
		replicas: replicas,  
		hash:     fn,  
		hashMap:  make(map[int]string),  
	}  
	if m.hash == nil {  
		m.hash = crc32.ChecksumIEEE  
	}  
	return m  
}

这里如果hash函数为空,那么默认调用32位的CRC校验值

实现add函数
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  
		}  
	}  
	sort.Ints(m.keys)  
}
  • add() : 用于向哈希表中添加一个或多个键值对。该方法接受一个或多个 string 类型的键作为参数,并将这些键映射到哈希表中的位置。
实现Get函数
func (m *Map) Get(key string) string {  
	if len(m.keys) == 0 {  
		return ""  
	}  
  
	hash := int(m.hash([]byte(key)))  
	// Binary search for appropriate replica.  
	idx := sort.Search(len(m.keys), func(i int) bool {  
		return m.keys[i] >= hash  
	})  
  
	return m.hashMap[m.keys[idx%len(m.keys)]]  
}
  • Get() : 传入字符串,得到哈希值字符串
  • 首先判断是否存在key
  • 用哈希函数计算哈希值
  • 二分查找找到第一个匹配的虚拟节点的下标 idx,从 m.keys 中获取到对应的哈希值
  • 通过hashmap映射得到真实节点

实现代码和测试代码

consistenthash.go
package consistenthash
import (

    "hash/crc32"

    "sort"

    "strconv"

)
type Hash func(data []byte) uint32
type Map struct {

    hash     Hash

    replicas int

    keys     []int // Sorted

    hashMap  map[int]string

}
func New(replicas int, fn Hash) *Map {

    m := &Map{

        replicas: replicas,

        hash:     fn,

        hashMap:  make(map[int]string),

    }

    if m.hash == nil {

        m.hash = crc32.ChecksumIEEE

    }

    return m

}

  

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

    if len(m.keys) == 0 {

        return ""

    }

  

    hash := int(m.hash([]byte(key)))

    // Binary search for appropriate replica.

    idx := sort.Search(len(m.keys), func(i int) bool {

        return m.keys[i] >= hash

    })

  

    return m.hashMap[m.keys[idx%len(m.keys)]]

}

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

        }

    }

    sort.Ints(m.keys)

}
test_consistenthash
package consistenthash  
  
import (  
	"strconv"  
	"testing"  
)  
  
func TestHashing(t *testing.T) {  
	hash := New(3, func(key []byte) uint32 {  
		i, _ := strconv.Atoi(string(key))  
		return uint32(i)  
	})  
  
	// Given the above hash function, this will give replicas with "hashes":  
	// 2, 4, 6, 12, 14, 16, 22, 24, 26  
	hash.Add("6", "4", "2")  
  
	testCases := map[string]string{  
		"2":  "2",  
		"11": "2",  
		"23": "4",  
		"27": "2",  
	}  
  
	for k, v := range testCases {  
		if hash.Get(k) != v {  
			t.Errorf("Asking for %s, should have yielded %s", k, v)  
		}  
	}  
  
	// Adds 8, 18, 28  
	hash.Add("8")  
  
	// 27 should now map to 8.  
	testCases["27"] = "8"  
  
	for k, v := range testCases {  
		if hash.Get(k) != v {  
			t.Errorf("Asking for %s, should have yielded %s", k, v)  
		}  
	}  
  
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值