为了账号安全,请及时绑定邮箱和手机立即绑定

自命为缓存之王的Caffeine(3)

标签:
Java

您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~


缓存的存储空间是远远小于磁盘的所以对于有些过期的数据就需要定期进行清理腾出存储空间Caffeine又是怎么做的呢

Caffeine和进程外缓存Redis一样也有主动失效和被动失效主动失效就是数据过期后由淘汰算法自动清除被动失效就是由开发同学手动清理了但这种被动失效还可以增加一些自定义功能也就是增加回调事件

普通的被动失效只需要调用invalidate()方法就行了

https://img1.sycdn.imooc.com//63e7acee0001682f09300571.jpg 

 

如果需要增加一些自定义功能那么可以增加一个移除监听器

https://img1.sycdn.imooc.com//63e7acf300016ed408840599.jpg

 

Guava一样,Caffeine也提供了一个仪表盘来监控缓存状态:

hitRate():返回命中与请求的比率;

hitCount(): 返回命中缓存的总数;

evictionCount():缓存逐出的数量;

averageLoadPenalty():加载新值所花费的平均时间。

https://img1.sycdn.imooc.com//63e7acf800017c7809530607.jpg

 

Caffeine作为进程内缓存也吸收了进程外缓存Redis的一些特性比如持久化Caffeine只做到了半持久化它可以充当一个底层资源的代理,也就是说可以通过Caffeine操作其他存储资源。Caffeine把操作缓存和操作外部资源这两种操作,封装成一个同步的原子性操作。

https://img1.sycdn.imooc.com//63e7acff0001d42b09620568.jpg

 

其中CacheWriter可能用来集成多个缓存进而实现多级缓存,受害者缓存(Victim Cache)将被删除数据被写入二级缓存。

了解了被动失效之后再来看看Caffeine的主动失效算法

常见的淘汰算法包括下面这些

1、FIFOFirst In First Out,先进先出算法,就是谁先来就淘汰谁,众生平等,只能顺序读写,使用也非常简单,这是最早使用的淘汰算法,问题在于如果某些数据经常使用,也会被淘汰掉。

https://img1.sycdn.imooc.com//63e7ad0300012fe306110126.jpg

2、LRU:Least Recently Used,最近最少使用算法,就是谁用得最少就淘汰谁,这是目前主流的缓存淘汰算法,问题在于,如果某些数据隔一段时间就会被频繁使用,也会被淘汰掉。

https://img1.sycdn.imooc.com//63e7ad07000182a506130189.jpg

3、LFU:Least Frequently Used,最近不经常使用算法,就是淘汰最近用得最少的那些数据,这是对LRU的一种改进,但同样也有问题,就是某个数据如果只是一开始出现的很频繁,但后面再也没出现过,那么仍然可能被缓存着。

https://img1.sycdn.imooc.com//63e7ad0b0001b4ca06140248.jpg

LFU两个很明显的缺点:

1、需要维护大而复杂的元数据来实现算法,而且每次访问都需要更新,开销巨大;

2、如果访问频率变化,LFU的频率信息却无法随之变化,造成命中率急剧下降。

为了解决LFU算法存储开销大的问题,TinyLFU出现了。TinyLFU利用Count-Min Sketch算法维护近期访问数据的频率信息,可以在具有较大访问量的场景下近似的替代LFU的数据统计部分,其原理有些类似布隆过滤器(Bloom Filter)。

Bloom Filter是一种空间利用效率很高的随机数据结构,它能用bit数组很简洁地表示一个集合:

1、使用一个大的bit数组存储所有key,每一个key通过多次不同的hash计算映射到数组的不同bit位;

2、如果key存在就将对应的bit位设置增加1,这样就可以通过少量的存储空间进行大量的数据过滤;

3、TinyLFU把多个bit位看做一个整体,用来统计key的使用频率,在读取时,取映射的所有值中的最小的值作为key的使用频率。

比如针对key的hash计算结果会映射到下面的bit数组中,TinyLFU会读取数值最小的h2(a)作为key的使用频率数据:

https://img1.sycdn.imooc.com//63e7ad100001b40805160241.jpg

 

Caffeine中维护了一个4-bit的Count-Min Sketch用来记录key的使用频率,这也意味着统计的key最大使用频率为15。

TinyLFU仍然有一个缺点,也就是LFU的第二个问题:如果访问频率突变,会造成缓存命中率的急剧下降,比如微博热点事件,某些词当天被搜索10W次,但是热度过去了,可能就再也不会出现了,然而相关数据却依然还在缓存中没被清理。

W-TinyLFU正是为了解决这类数据过期问题而诞生,它由两部分组成:

1、窗口缓存Window Cache使用没有任何回收策略的LRU,占总缓存大小的1%,用于存储新到来的数据,主要为应对短期流量突发的访问场景;

2、主缓存Main Cache使用分段SLRU + TinyLFU,占总缓存大小的99%;

3、其中分段SLRU又被分为两个区:Probation区,用于存储比较冷门的数据,占用主缓存20%空间,另一个Protected区,用于存储比较热门的数据,占用主缓存80%空间。

具体的淘汰过程是

1、新添加的数据首先放入窗口缓存Window Cache(LRU)中,同时由TinyLFU完成计数;

2、如果Window Cache满了,就把Window Cache淘汰的数据转移到主缓存Probation区中;

3、如果Probation区还未满,并且其中的数据在后续操作中再次被访问时,那么该条数据会进入Protected区;

4、如果Probation区也满了,就比较从窗口缓存Window Cache转移过来的数据(候选者)和Probation要淘汰的数据(受害者);

5、如果Protected区也满了,那么会按照LRU策略将数据驱逐到Probation区。

https://img1.sycdn.imooc.com//63e7ad1400018c8909540599.jpg

不得不说,W-TinyLFU算法的淘汰过程和JVM GC过程非常像:

1、在区域划分上:Window Cache对应S0和S1、Probation区对应Eden区、Protected区对应老年代;

2、在数据淘汰流程上:先进入Window Cache(S0和S1)、再进入Probation区(Eden区)或从Probation区淘汰、再进入Protected区(老年代)或从Protected区淘汰。

 


感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消