高性能本地缓存Ristretto(一)——存储策略

Ristretto是Dgraph基于golang实现的一个高性能的本地缓存库。特点是高命中率,高吞吐量,可自定义存储成本,支持一些自定义回调函数,并提供了较多的统计信息。

本文将主要讲述Ristretto的存储策略的实现方式。

题外话:Ristretto 意为“意式特浓咖啡”,感觉在跟java实现的”caffine(咖啡因)“叫板,HAHAHA。。。咯

确定存储结构

golang中的map是非并发安全的,要实现并发安全,有三种常用策略:加锁,sync.Map, 分片字典。对于三种策略有如下的分析:

加锁:要缓存的数据,绝对是频繁访问的数据,加锁必然影响性能,失去了缓存的意义,所以加锁是下策。

sync.Map:sync.Map 内部通过两个map+锁(本质上还是缓存+锁),实现了并发安全,但是对于读写都多的场景,性能依然堪忧,此乃中策。

分片字典:分片字典的是将数据散列在多个map中,尽量降低数据出现竞态的概率。对于其中每一个map还是采用加锁策略。在读多写多的场景中,也有较好的表现,此乃上策。

Ristretto中的分片字典

为了实现并发安全且高性能的存储需求,Ristretto使用了分片字典策略。

在Ristretto中与存储有关的数据结构,如下(省略了部分信息):

type shardedMap struct {
	shards       []*lockedMap  // map类型的slice。共256个
    ...
}

type lockedMap struct {
	sync.RWMutex        		// 每个分片的map,包含独立的锁
	data         map[uint64]storeItem // 
	...
}

type storeItem struct {     // map 中存储的数据类型
	key        uint64
	conflict   uint64
	value      interface{}
	... 
}

请添加图片描述

新增一个Key

以 key为string, value 为结构体为例:

key :=  "ZHH"
value := PeosonInfo{
    FallName : "ZhaoHaihang",
    Phone : "13112341234",
    Age : 18,
    Addres : "XXXXX",

}

整体流程:

key 先经过keyToHash()得到 keyHash 和conflictHash,通过keyHash %256 确定该key应该存在哪一个lockedmap中, 存之前先判断key是不是已经在里面,如果已经在了走更新逻辑,否则走新增逻辑。

详细流程:

结合代码看详细的流程是最清晰的。

  1. 首先调用cache.go中的Set()函数:

其中参数cost表示存储该key的代价,可暂时忽略请添加图片描述
2. 接着内部调用了SetWithTTL():

其中参数ttl为该key的过期时间,可暂时忽略。(Ristretto是支持设置key的过期时间的,有关过期策略的具体实现,准备放在下一篇文章,敬请期待)
请添加图片描述
3. 继续调用setInternal():

onlyUpdata仅作为一个功能开关,只与更新有关,可暂时忽略。
keyToHash()得到的两个值,可以简单看作是两种hash函数得到的值,第二个是为了解决哈希冲突存在的。图中… 表示一些不涉及主要逻辑的省略 (ps:对于两个key,使用两种哈希函数的得到相同结果的概率是极低的)。
请添加图片描述
4. 在初始化时,会启动一个协程执行processItems()函数。在函数中不断从第三步中提到的setBuf取数据,并根据操作类型执行相应的逻辑。新增的逻辑为c.store.Set()
请添加图片描述
请添加图片描述
5. 接着来到store.go中的Set()函数:

Set() 主要用于根据keyToHash获得的keyHash,并通过取模确定Key应该在具体的哪一个LoackedMap。其中numShards为常量=256。即整个shards中包含256个LoadckedMap。

此处要注意区分几个key不同的含义,不要混淆。
请添加图片描述
请添加图片描述
6. 最后来到lockedMap的Set()函数:
此处是最终写操作执行的地方。
同时此处有个问题:刚才说在第三步中updata函数已经判断了一次key是否存在,为何这里要再次判断?
答:在并发的情况下,刚才判断的结果,已经不能保证是最新的结果了。无法保证是不是有其他协程抢先写了一条数据,所以要加锁后,再次判断。
请添加图片描述
最后,如果能明白插入操作是怎么运行的 ,相信对于明白读、更新、删除的流程就非常容易了,也大致是这个过程,这里就不再赘述了。

写在最后:本人能力有限,如果有错误之处,还望各位及时指正,谢谢!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值