Golang分段锁

简介: Golang分段锁

介绍


因为golang的原生map是非并发安全的,所以为了保证map的并发安全,最简单的方式就是给map加锁。直接对一个map加锁,当访问map的请求越来越多,都竞争这一把锁使得并发访问变慢。


分段锁是一种锁的设计,并不是具体的一种锁,分段锁顾名思义就是将锁分段,将锁的粒度变小,将存储的对象分散到各个分片(shard)中,每个分片由一把锁控制,我们将 key 分散到固定数量的 shard 中避免 rehash 操作。shard 是有锁保护的 map, 当 shard 进行 rehash 时会阻塞shard内的读写,但不会对其他 shard 造成影响。这样使得当需要对在A分片上的数据进行读写时不会影响B分片的读写。


虽然有sync.Map存在,但是通过压力测试对比,分段锁的性能更好,下面给出分段锁代码

代码

package main
import (
  "math"
  "sync"
  "sync/atomic"
)
type ConcurrentMap struct {
  shard      []*MapShard
  count      int32
  shardCount uint32
}
type MapShard struct {
  m     map[string]interface{}
  mutex sync.RWMutex
}
//该参数转成二进制,每个位都赋为1
func computeCapacity(param int) int {
  if param <= 16 {
    return 16
  }
  n := param - 1
  n |= n >> 1
  n |= n >> 2
  n |= n >> 4
  n |= n >> 8
  n |= n >> 16
  if n < 0 {
    return math.MaxInt32
  }
  return n + 1
}
// MakeConcurrentMap 返回一个分段锁的实例
func MakeConcurrentMap(shardCount int) *ConcurrentMap {
  shardCount = computeCapacity(shardCount)
  shard := make([]*MapShard, shardCount)
  for idx := range shard {
    shard[idx] = &MapShard{
      m:     make(map[string]interface{}),
      mutex: sync.RWMutex{},
    }
  }
  return &ConcurrentMap{
    shard:      shard,
    count:      0,
    shardCount: uint32(shardCount),
  }
}
const prime32 = uint32(16777619)
func fnv32(key string) uint32 {
  hash := uint32(2166136261)
  for i := 0; i < len(key); i++ {
    hash *= prime32
    hash ^= uint32(key[i])
  }
  return hash
}
func (dict *ConcurrentMap) getShardMap(key string) *MapShard {
  hashCode := fnv32(key)
  idx := (dict.shardCount - 1) & hashCode
  return dict.shard[idx]
}
func (dict *ConcurrentMap) Get(key string) (val interface{}, exists bool) {
  shard := dict.getShardMap(key)
  shard.mutex.RLock()
  defer shard.mutex.RUnlock()
  val, exists = shard.m[key]
  return
}
func (dict *ConcurrentMap) Len() int {
  return int(atomic.LoadInt32(&dict.count))
}
// Set 插入
func (dict *ConcurrentMap) Set(key string, val interface{}) int {
  shard := dict.getShardMap(key)
  shard.mutex.Lock()
  defer shard.mutex.Unlock()
  if _, ok := shard.m[key]; ok {
    shard.m[key] = val
    return 0
  }
  shard.m[key] = val
  atomic.AddInt32(&dict.count, 1)
  return 1
}
// Remove 删除一个key
func (dict *ConcurrentMap) Remove(key string) int {
  shard := dict.getShardMap(key)
  shard.mutex.Lock()
  defer shard.mutex.Unlock()
  if _, ok := shard.m[key]; ok {
    delete(shard.m, key)
    atomic.AddInt32(&dict.count, -1)
    return 1
  }
  return 0
}
目录
相关文章
|
18天前
|
安全 Go
Golang深入浅出之-互斥锁(sync.Mutex)与读写锁(sync.RWMutex)
【4月更文挑战第23天】Go语言并发编程中,`sync.Mutex`和`sync.RWMutex`是保证线程安全的关键。互斥锁确保单个goroutine访问资源,而读写锁允许多个读者并发访问。常见问题包括忘记解锁、重复解锁以及混淆锁类型。使用`defer`可确保解锁,读写锁不支持直接升级或降级,需释放后再获取。根据读写模式选择合适锁以避免性能下降和竞态条件。理解并正确使用锁是编写并发安全程序的基础。
25 3
|
18天前
|
Go
Golang 中的互斥锁是什么?
# go # programming # beginners # architecture
19 0
|
18天前
|
存储 Go
浅谈Golang互斥锁sync.Mutex
浅谈Golang互斥锁sync.Mutex
19 0
|
18天前
|
存储 编译器 Go
Golang底层原理剖析之互斥锁sync.Mutex
Golang底层原理剖析之互斥锁sync.Mutex
43 0
|
8月前
|
Go
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
Golang 语言标准库 sync 包的 RWMutex 读写互斥锁怎么使用?
28 0
|
11月前
|
安全 Go
golang中的互斥锁和管道
golang中的互斥锁和管道
|
11月前
|
存储 Go 调度
golang 锁原理剖析,你值得收藏
golang 锁原理剖析,你值得收藏
|
11月前
|
Go
golang内存模型-2 锁解决Happens Before
golang内存模型-2 锁解决Happens Before
|
存储 Go
Golang中互斥锁和读写互斥锁
Golang中互斥锁和读写互斥锁
73 0
http://www.vxiaotou.com