Skip to content

Commit 6a3f10b

Browse files
author
jerekli
committed
Day4 一致性哈希学习笔记
1 parent cf36443 commit 6a3f10b

File tree

2 files changed

+20
-10
lines changed

2 files changed

+20
-10
lines changed

gee-cache/day4-consistent-hash/geecache/consistenthash/consistenthash.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ type Hash func(data []byte) uint32
1111

1212
// Map constains all hashed keys
1313
type Map struct {
14-
hash Hash
15-
replicas int
16-
keys []int // Sorted
17-
hashMap map[int]string
14+
hash Hash // 定义了函数类型 Hash,
15+
replicas int // 虚拟节点倍数
16+
keys []int // Sorted 哈希环
17+
hashMap map[int]string // 虚拟节点与真实节点的映射表 hashMap,键是虚拟节点的哈希值,值是真实节点的名称。
1818
}
1919

20-
// New creates a Map instance
20+
// New creates a Map instance 构造函数 New() 允许自定义虚拟节点倍数和 Hash 函数。
2121
func New(replicas int, fn Hash) *Map {
2222
m := &Map{
2323
replicas: replicas,
2424
hash: fn,
2525
hashMap: make(map[int]string),
2626
}
2727
if m.hash == nil {
28-
m.hash = crc32.ChecksumIEEE
28+
m.hash = crc32.ChecksumIEEE // 采取依赖注入的方式,允许用于替换成自定义的 Hash 函数,也方便测试时替换,默认为 crc32.ChecksumIEEE 算法。
2929
}
3030
return m
3131
}
@@ -34,11 +34,15 @@ func New(replicas int, fn Hash) *Map {
3434
func (m *Map) Add(keys ...string) {
3535
for _, key := range keys {
3636
for i := 0; i < m.replicas; i++ {
37+
// 对每一个真实节点 key,对应创建 m.replicas 个虚拟节点,虚拟节点的名称是:strconv.Itoa(i) + key,即通过添加编号的方式区分不同虚拟节点。
3738
hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
39+
// 使用 m.hash() 计算虚拟节点的哈希值,使用 append(m.keys, hash) 添加到环上。
3840
m.keys = append(m.keys, hash)
41+
// 在 hashMap 中增加虚拟节点和真实节点的映射关系
3942
m.hashMap[hash] = key
4043
}
4144
}
45+
// 最后一步,环上的哈希值排序。
4246
sort.Ints(m.keys)
4347
}
4448

@@ -47,12 +51,16 @@ func (m *Map) Get(key string) string {
4751
if len(m.keys) == 0 {
4852
return ""
4953
}
50-
54+
// 第一步,计算 key 的哈希值。
5155
hash := int(m.hash([]byte(key)))
5256
// Binary search for appropriate replica.
57+
// 第二步,顺时针找到第一个匹配的虚拟节点的下标 idx,从 m.keys 中获取到对应的哈希值。如果 idx == len(m.keys),
58+
// 说明应选择 m.keys[0],
5359
idx := sort.Search(len(m.keys), func(i int) bool {
60+
// 寻找到第一个大于这个hash值的keys[i]的坐标idx
5461
return m.keys[i] >= hash
5562
})
56-
63+
// 第三步,通过 hashMap 映射得到真实的节点。
64+
// 因为 m.keys 是一个环状结构,所以用取余数的方式来处理这种情况。
5765
return m.hashMap[m.keys[idx%len(m.keys)]]
5866
}

gee-cache/day4-consistent-hash/geecache/consistenthash/consistenthash_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,22 @@ import (
66
)
77

88
func TestHashing(t *testing.T) {
9+
// 自定义的 Hash 算法只处理数字,传入字符串表示的数字,返回对应的数字即可。
910
hash := New(3, func(key []byte) uint32 {
1011
i, _ := strconv.Atoi(string(key))
1112
return uint32(i)
1213
})
1314

1415
// Given the above hash function, this will give replicas with "hashes":
1516
// 2, 4, 6, 12, 14, 16, 22, 24, 26
16-
hash.Add("6", "4", "2")
17-
17+
hash.Add("6", "4", "2") // 一开始,有 2/4/6 三个真实节点,对应的虚拟节点的哈希值是 02/12/22、04/14/24、06/16/26。
18+
//那么用例 2/11/23/27 选择的虚拟节点分别是 02/12/24/02,也就是真实节点 2/2/4/2。
1819
testCases := map[string]string{
1920
"2": "2",
2021
"11": "2",
2122
"23": "4",
2223
"27": "2",
24+
"3": "4",
2325
}
2426

2527
for k, v := range testCases {

0 commit comments

Comments
 (0)