MST

星途 面试题库

面试题:Go语言Mutex锁的性能优化

在高并发场景下,使用Go语言的Mutex锁可能会出现性能瓶颈。请分析可能导致性能瓶颈的原因,并提出至少两种优化方案,同时说明每种方案的适用场景。
18.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能导致性能瓶颈的原因

  1. 锁争用:高并发时多个 goroutine 频繁竞争同一把 Mutex 锁,导致大量 goroutine 处于等待状态,增加了上下文切换开销。
  2. 串行化执行:获得锁的 goroutine 在临界区执行时,其他 goroutine 必须等待,限制了并行性,在临界区执行时间较长时影响较大。

优化方案及适用场景

  1. 读写锁(RWMutex)
    • 适用场景:当读操作远多于写操作时。例如在缓存系统中,大量请求读取缓存数据,偶尔更新缓存。读操作可并发执行,写操作则独占锁,保证数据一致性。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

var (
    data int
    rwmu sync.RWMutex
)

func read(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.RLock()
    defer rwmu.RUnlock()
    fmt.Println("read data:", data)
}

func write(wg *sync.WaitGroup) {
    defer wg.Done()
    rwmu.Lock()
    defer rwmu.Unlock()
    data++
    fmt.Println("write data:", data)
}
  1. 分段锁
    • 适用场景:数据可按一定规则划分成多个独立部分,且不同部分的操作可并行进行。如在数据库分表场景下,对不同表的操作可使用不同的锁。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type ShardedMutex struct {
    shards []sync.Mutex
    numShards int
}

func NewShardedMutex(numShards int) *ShardedMutex {
    shards := make([]sync.Mutex, numShards)
    return &ShardedMutex{
        shards: shards,
        numShards: numShards,
    }
}

func (sm *ShardedMutex) Lock(key int) {
    index := key % sm.numShards
    sm.shards[index].Lock()
}

func (sm *ShardedMutex) Unlock(key int) {
    index := key % sm.numShards
    sm.shards[index].Unlock()
}
  1. 减少锁粒度
    • 适用场景:当临界区代码可进一步细分,不同部分的操作不相互影响时。例如在一个复杂的数据结构操作中,可对不同字段分别加锁。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type Data struct {
    field1 int
    field2 int
    mu1 sync.Mutex
    mu2 sync.Mutex
}

func updateField1(d *Data, wg *sync.WaitGroup) {
    defer wg.Done()
    d.mu1.Lock()
    defer d.mu1.Unlock()
    d.field1++
    fmt.Println("update field1:", d.field1)
}

func updateField2(d *Data, wg *sync.WaitGroup) {
    defer wg.Done()
    d.mu2.Lock()
    defer d.mu2.Unlock()
    d.field2++
    fmt.Println("update field2:", d.field2)
}