高性能本地缓存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 uint64conflict uint64value 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是不是已经在里面,如果已经在了走更新逻辑,否则走新增逻辑。
详细流程:
结合代码看详细的流程是最清晰的。
- 首先调用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是否存在,为何这里要再次判断?
答:在并发的情况下,刚才判断的结果,已经不能保证是最新的结果了。无法保证是不是有其他协程抢先写了一条数据,所以要加锁后,再次判断。
最后,如果能明白插入操作是怎么运行的 ,相信对于明白读、更新、删除的流程就非常容易了,也大致是这个过程,这里就不再赘述了。
写在最后:本人能力有限,如果有错误之处,还望各位及时指正,谢谢!
高性能本地缓存Ristretto(一)——存储策略相关推荐
- 高性能本地缓存Ristretto(二)——过期策略
ristretto提供了SetWithTTL()方法,支持创建key的同时,并设置一个过期时间. ristretto 利用嵌套的map结构,并结合巧妙的存储方式,实现了对每一个key的过期时间的管理. ...
- 高性能本地缓存Ristretto(三)——淘汰策略
之前提到过,缓存即 存储 和 淘汰策略.一个好的淘汰策略,对于提升命中率起着至关重要的作用.一般常用的淘汰策略有,LRU.LFU.TinyLFU以及Window-TinyLFU (讲解LRU.LFU. ...
- Java高性能本地缓存框架Caffeine
文章目录 Java高性能本地缓存框架Caffeine 如何使用 缓存加载 手动加载 自动加载 手动异步加载 自动异步加载 过期策略 基于大小 基于时间 基于引用 Caffeine.weakKeys() ...
- 「GoCN酷Go推荐」高性能内存缓存 ristretto
背景 ristretto 是 dgraph 团队开源的一款高性能内存缓存库,旨在解决高并发场景下的缓存性能和吞吐瓶颈.dgraph 专攻的方向是高性能图数据库,ristretto 就是其图数据库和 K ...
- 【微信小程序3】本地缓存:一次性存储多个对象值
一.缓存介绍 每个微信小程序都有自己的本地缓存.同一个微信用户,同一个小程序 storage 上限为 10MB.localStorage 以用户维度隔离,同一台设备上,A 用户无法读取到 B 用户的数 ...
- 干掉 GuavaCache:Caffeine 才是本地缓存的王
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 话说,中间件的选择上,Spring(SpringBoot ...
- ehcache缓存原理_干掉GuavaCache:Caffeine才是本地缓存的王
话说,中间件的选择上,Spring(SpringBoot)一直是业界的风向标.比如Spring一直使用「Jackson」,而没有使用Gson和fastjson.SpringBoot2.0默认数据库连接 ...
- Java本地缓存框架系列-Caffeine-1. 简介与使用
Caffeine 是一个基于Java 8的高性能本地缓存框架,其结构和 Guava Cache 基本一样,api也一样,基本上很容易就能替换. Caffeine 实际上就是在 Guava Cache ...
- Java8本地缓存Caffeine
文章目录 一.Caffeine介绍 1.缓存介绍 2.Caffeine介绍 二.Caffeine基础 1.缓存加载策略 1.1 Cache手动创建 1.2 Loading Cache自动创建 1.3 ...
最新文章
- 调用另一个python文件中的代码
- C++读取配置文件的写法
- openfire修改服务器名称方法
- .NET Core中异常过滤器ExceptionFilter的使用介绍
- [HDU 4666]Hyperspace[最远曼哈顿距离][STL]
- Mr.J--C语言经典编程100例
- D3 BarChart
- 免费手机电脑同屏神器——Mirroid
- 7.20 - 每日一题 - 408
- android 磁贴布局,拼图酱 - 通过布局、磁贴、滤镜等元素重新组合照片,充满乐趣 - Android 应用 - 图像 - 【最美应用】...
- X86 CPU 漏洞 Meltdown 原理及google攻击代码
- oracle dbms_lob trim,DBMS_LOB
- vs 的 tfs 账号更改
- 【C语言程序设计】基本算术运算
- 解决Dmaven.multiModuleProjectDirectory system propery is not set. Check M2_HOME错误
- GOPATH 与工作空间
- 舒尔特注意力训练网页版
- 启动mysql报错:mysql.service: Service hold-off time over, scheduling restart
- 统一数据交换(UDX)
- Android实现联动下拉框