Golang - 分段锁实现并发安全Map
一.引言
我们一般有两种方式来降低锁的竞争:
- 第一种:减少锁的持有时间,sync.Map即是采用这种策略,通过冗余的数据结构,使得需要持有锁的时间,大大减少。
- 第二种:降低锁的请求频率,锁分解和锁分段技术即是这种思想的体现。
锁分段技术又可称为分段锁机制
什么叫做分段锁机制?
将数据分为一段一段的存储,然后给每一段数据配备一把锁.
这样在多线程情况下,不同线程操作不同段的数据不会造成冲突,线程之间也不会存在锁竞争,有效的提高了并发访问的效率
二.实现原理
首先我们需要给数据进行分段,属于同一个段的数据放在一起,我们采用golang原生的map来充当段的容器,用于存储元素,将key通过哈希映射的形式分配到不同的段中。
// SharedMap 并发安全的小map,ShardCount 个这样的小map数组组成一个大map
type SharedMap struct {
items map[string]interface{}
sync.RWMutex // 读写锁,保护items
}
可以看到,我们给每个段都配备了一个内置的读写锁,用于保护段内的数据安全
// ShardCount 底层小shareMap数量
var ShardCount = 32
// ConcurrentHashMap 并发安全的大map,由 ShardCount 个小mao数组组成,方便实现分段锁机制
type ConcurrentHashMap []*SharedMap
然后 ShardCount 个ShareMap就组成了一个大的并发安全的map。
其哈希函数采用了著名的fnv函数
// fnv32 hash函数
func fnv32(key string) uint32 {
// 著名的fnv哈希函数,由 Glenn Fowler、Landon Curt Noll和 Kiem-Phong Vo 创建
hash := uint32(2166136261)
const prime32 = uint32(16777619)
keyLength := len(key)
for i := 0; i < keyLength; i++ {
hash *= prime32
hash ^= uint32(key[i])
}
return hash
}
1.New
// New 创建一个新的concurrent map.
func New() ConcurrentHashMap {
m := make(ConcurrentHashMap, ShardCount)
for i := 0; i < ShardCount; i++ {
m[i] = &SharedMap{items: make(map[s