0%

如何使用Go实现支持并发的Map

原理设计

由于考虑到使用单独map来做会导致并发性能非常大,比如当并发写的情况下,由于使用锁机制,这样并发写的性能就会受到很大的影响。于是我们设计并发map时考虑采取多个map的方案,按照上图那样我们会根据key值进行hash分区,来判断用于哪一个innermap,这样就会对并发性能由很大的提升。

代码复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package concurrent_map

import (
"sync"
)

type innerMap struct {
m map[interface{}]interface{}
lock sync.RWMutex
}

//这个接口用于给用户自己设计key,hash方法也可以自己设计
type Concurrent_Key interface {
RawKey() interface{} //原始的key值
KeyAfterPartitioned() int64 //原始key值经过hash运算后得到的结果
}

type ConcurrentMap struct {
partitions_map []*innerMap
numsOfPartitions int64 //记录分区数量
}

func CreatConcurrentMap(numsOfPartitions int64) *ConcurrentMap {
var partitions_map []*innerMap
for i := 0; i < int(numsOfPartitions); i++ {
partitions_map = append(partitions_map, &innerMap{
m: make(map[interface{}]interface{}),
})
}
return &ConcurrentMap{
partitions_map: partitions_map,
numsOfPartitions: numsOfPartitions,
}
}

//下面是concurrentMap的一些基本使用方式,由于innermap是内
//部map,因此它的操作方法都应该首字母小写
func (im *innerMap) get(key Concurrent_Key) (interface{}, bool) {
im.lock.RLock()
val, ok := im.m[key.RawKey()]
im.lock.RUnlock()
return val, ok
}

func (im *innerMap) set(key Concurrent_Key, val interface{}) {
im.lock.Lock()
im.m[key.RawKey()] = val
im.lock.Unlock()
}

func (im *innerMap) del(key Concurrent_Key) {
im.lock.Lock()
delete(im.m, key.RawKey())
im.lock.Unlock()
}

//这个方法用于获取分区
func (cm *ConcurrentMap) getPartition(key Concurrent_Key) *innerMap {
return cm.partitions_map[key.KeyAfterPartitioned()%cm.numsOfPartitions]
}

//下面的方法需要大写首字母
func (cm *ConcurrentMap) Get(key Concurrent_Key) (interface{}, bool) {
im := cm.getPartition(key)
return im.get(key)
}

func (cm *ConcurrentMap) Set(key Concurrent_Key, val interface{}) {
im := cm.getPartition(key)
im.set(key, val)
}

func (cm *ConcurrentMap) Del(key Concurrent_Key) {
im := cm.getPartition(key)
im.del(key)
}

我们要使用上面的并发map,需要自己实现Concurrent_Key接口,下面是一个案例

1
2
3
4
5
6
7
8
9
10
11
type StringPartitionedKey struct {
val string
}

func (key *StringPartitionedKey) RawKey() interface{} {
return key.val
}

func (key *StringPartitionedKey) KeyAfterPartitioned() int64{
return hash(key.val)
}

关于hash的实现可以参考我的JackTan25/MurmurHashTest3