0%

无锁缓存

场景

高并发写多读少的场景下,对 map 的写需要加锁,急剧影响性能.

解决方案

方案1 | 单库变多库.

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
package main

import (
"sync"
)

type driverInfo struct {
id int
age int
}

type company struct {
lock []sync.RWMutex
driver map[int]map[int]*driverInfo
count int
}

func newCompany(count int) *company {
lock := make([]sync.RWMutex, count)
driver := make(map[int]map[int]*driverInfo, count)
for i := 0; i < count; i++ {
lock[i] = sync.RWMutex{}
driver[i] = make(map[int]*driverInfo)
}

return &company{
lock: lock,
count: count,
driver: driver,
}
}

func (c *company) getDriver(id int) (derive *driverInfo) {
i := id % c.count
c.lock[i].RLock()
defer c.lock[i].RUnlock()
return c.driver[i][id]
}

func (c *company) setDriver(driver *driverInfo) {
i := driver.id % c.count
c.lock[i].Lock()
defer c.lock[i].Unlock()
c.driver[i][driver.id] = driver
}

方案 2 | 库变表

跟方案 1 类似,减小锁的粒度到字段.潜在的问题是当数据量过大的,锁需要占用大量内存.

方案 3 | 不加锁, 加签名校验数据的合法性

在并发场景下,对同一个值的修改,数据的完整性不能保证.对 key 的 val1,val2 的写入,可能写入val1 成功一半,val2 成功一半,即 val1 half+ val2 half.为了保证数据的正确性.可以在数据的末尾增加 crc32 校验,计算数据的签名.取出数据时,需要校验签名是否正确.签名不正确,直接丢弃,从数据库拉取.

参考链接