concurrentcache--golang内存缓存

concurrentcache是什么

concurrentcache是golang版的内存缓存库,采用多Segment设计,支持不同Segment间并发写入,提高读写性能。

设想场景

内存缓存的实现,防止并发写冲突,都需要先获取写锁,再写入。如果只有一个存储空间,那么并发写入的时候只能有一个go程在操作,其他的都需要阻塞等待。为了提高并发写的性能,把存储空间切分成多个Segment,每个Segment拥有一把写锁,那样,分布在不同Segment上的写操作就可以并发执行了。concurrentcache设计就是切分多个Segment,提高些并发写的效率。

concurrentcache设计思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ConcurrentCache结构体,包含一个ConcurrentCacheSegment切片
type ConcurrentCache struct {
segment []*ConcurrentCacheSegment
sCount uint32
}
// ConcurrentCacheSegment结构体,内部使用一个map存储数据
type ConcurrentCacheSegment struct {
sync.RWMutex
data map[string]*ConcurrentCacheNode
lvPool map[string]*ConcurrentCacheNode
dCount uint32
dLen uint32
pool *sync.Pool
hits uint64
miss uint64
now time.Time
}
type ConcurrentCacheNode struct {
V interface{}
visit uint32
lifeExp time.Duration
createTime time.Time
}

从ConcurrentCache结构体可以看出,包含一个ConcurrentCacheSegment切片,sCount表示切片的长度,这个切片的长度,在初始化的时候就确定,不再改变。
ConcurrentCacheSegment内包含一个读写锁,写入时候根据key的hash值选取写入到哪一个Segment内,通过多Segment设计,来提高写并发效率。

为了提高读效率,尽可能多的减少读过程对内存缓存的修改,使用golang提供的原子操作来修改访问状态(visit、miss、hits等),遇到缓存过期的节点也不马上淘汰,因为读访问上的是读锁,要删除数据需要用到写互斥锁,这样会降低读的并发性,所以推迟删除过期的节点,当写数据,Segment的节点不够用的时候再去删除过期节点。

concurrentcache缓存淘汰方式

concurrentcache缓存淘汰方式采用随机选取3个节点,优先淘汰其中过期的一个节点(上一步说的推迟删除过期节点),如果没有过期节点就选取访问量最少(ConcurrentCacheNode结构体中的visit)的节点淘汰。这个思路是参考redis的缓存淘汰策略,redis并不是严格lru算法,采用的是随机选取样本的做法。这样做也是为了提高写性能。

concurrentcache & cache2go 性能对比

(MBP 16G版本)
concurrentcache和cache2go进行了并发下的压测对比,对比结果,concurrentcache无论是执行时间还是内存占比,都比cache2go优

concurrentcache

1
2
3
4
5
6
BenchmarkConcurrentCache_Set-8 3000000 573 ns/op 190 B/op 2 allocs/op
BenchmarkConcurrentCache_Set-8 2000000 634 ns/op 235 B/op 3 allocs/op
BenchmarkConcurrentCache_Set-8 3000000 535 ns/op 190 B/op 2 allocs/op
BenchmarkConcurrentCache_Get-8 5000000 235 ns/op 36 B/op 1 allocs/op
BenchmarkConcurrentCache_Get-8 5000000 234 ns/op 36 B/op 1 allocs/op
BenchmarkConcurrentCache_Get-8 5000000 234 ns/op 36 B/op 1 allocs/op

cache2go

1
2
BenchmarkCacheTable_Add-8 1000000 1858 ns/op 480 B/op 10 allocs/op
BenchmarkCacheTable_Value-8 5000000 300 ns/op 60 B/op 2 allocs/op

concurrentcache源码