1.什么是“重锁”(Heavy Lock)
在 Go 性能调优或并发编程中,我们常说的 “重锁”(heavy lock)不是官方术语,而是一个工程上的概念,指的是:锁竞争严重、临界区较大、持锁时间较长的互斥锁(sync.Mutex)。
1.多个 goroutine 同时频繁地去争夺同一把锁;
2.加锁的代码块中做了比较“重”的操作(比如 I/O、JSON 编码、数据库操作);
导致 goroutine 阻塞、上下文切换频繁,最终造成性能瓶颈。
2.为什么会出现“重锁”问题
1.临界区太大(锁保护的范围过广);
2.频繁写操作导致锁争用;
3.使用全局变量或共享状态;
4.没有分片(sharding)或局部化锁机制;
5.锁中包含耗时操作(例如网络请求、磁盘 I/O)。
var mu sync.Mutex
var cache = make(map[string]string)
func Set(k, v string) {
mu.Lock()
defer mu.Unlock()
cache[k] = v
}
#当高并发调用 Set() 时,所有 goroutine 都在争抢同一把 mu,这就形成“重锁”。
3.优化思路与替代方案
3.1 使用 sync.Map
适用于读多写少的场景:
var m sync.Map
m.Store("a", 1)
v, _ := m.Load("a")
#sync.Map 内部采用分片和原子操作,避免了全局锁竞争。
3.2 使用原子操作(sync/atomic)
适用于简单的计数、标志位等操作:
var count int64
atomic.AddInt64(&count, 1)
#无锁化操作,性能更高,且不阻塞其他 goroutine。
3.3 优化锁粒度(细化锁)
将一把全局锁拆分成多把局部锁:
var locks [16]sync.Mutex
func getLock(key string) *sync.Mutex {
return &locks[hash(key)%16]
}
#减少锁争用,提高并发性能。