【Caffeine缓存】Caffeine缓存淘汰算法Window-TinyLFU详解
Caffeine的缓存淘汰策略是什么?Window-TinyLFU算法的原理是什么?是如何工作的?看完本文,相信你会得到答案。
前言
Caffeine 因为使用了 Window-TinyLFU 缓存淘汰策略,提供了一个近乎最佳的命中率。综合了 LRU 和 LFU 算法的长处,使其成为本地缓存之王。
1.算法框架
下面两幅图都是 W-TinyLFU 算法的示意图,对比着看去理解。
在 W-TinyLFU 算法中,将整个缓存区划分为两大区域:Window Cache
和 Main Cache
。
Window Cache 是一个标准的 LRU 缓存,只占整个缓存内存空间大小的 1% ;
而 Main Cache 则是一个 SLRU (Segmented LRU) ,占整个缓存内存空间大小的 99% ,是缓存的主要区域。里面进一步被划分成两个区域:Probation Cache
观察区和 Protected Cache
保护区。Probation 观察区占 Main Cache 大小的 20%;而 Protected 保护区占 Main Cache 大小的 80% ,是 Main Cache 的主要区域。
【小贴士】
- W-TinyLFU 算法中的 3 个区域 ( Window Cache, Probation Cache 和 Protected Cache ) 中的缓存数据都是可以被访问到的。千万不要理解成只有 Protected Cache 中的缓存能被访问。
- W-TinyLFU 算法中的每一个缓存项,都由 2 部分组成,分别是:
- 数据:缓存数据本身;
- 访问频次:被访问的次数。
2.算法流程
-
新增缓存数据首先写入 Window Cache 区域。当 Window Cache 空间满时,LRU 算法发挥作用,最久未被访问的缓存项会被移出 Window Cache 。这个被移出的缓存项称为 Candidate (候选项),将面临 2 种情况:
- 如果 Probation Cache 未满,从 Window Cache 中移出的缓存项会直接写入 Probation Cache 中;
- 如果 Probation Cache 已满,则进入到 TinyLFU 过滤器,与 Protected Cache 移出的缓存项比较。根据 TinyLFU 算法确定该缓存项是淘汰还是写入 Probation Cache,后面会说。
-
Probation Cache 中的缓存项的访问频率达到一定次数后,就会晋升到 Protected Cache 中。(Ps. 到 Protected 保护区的都是经过 2 层选拔的真热门数据)如果 Protected Cache 也满了,根据 LRU 算法,最久未被访问的缓存项会被移出 Protected Cache ,降级成为 Candidate ,进入到 TinyLFU 过滤器,与 Window Cache 移出的缓存项作比较,根据 TinyLFU 算法确定该缓存项是淘汰还是写入到 Probation Cache 中。
-
此时把目光聚焦到 TinyLFU 过滤器,它汇聚了来自 Window Cache 、Protected Cache 和 Probation Cache 3 个区域中被移出的缓存项。其中,从 Window Cache 和 Protected Cache 中移出的缓存项称为 Candidate ;而从 Probation Cache 中移出的缓存项称为 Victim (受害者) 。这 3 个区域移出的缓存项都是各自区域中最久未被使用的数据,在 TinyLFU 过滤器中进行竞争,竞争算法如下:
- 如果 Candidate 的访问频率 > Victim 的访问频率,则直接淘汰 Victim ;
- 如果 Candidate 的访问频率 <= Victim 的访问频率,此时分为两种情况:
- 如果 Candidate 的访问频率 < 5 ,则淘汰 Candidate ;
- 如果 Candidate 的访问频率 >= 5 ,则在 Candidate 和 Victim 中随机淘汰一个。
获胜者被称为 Winner ,就会被写入到 Probation Cache 中。
-
从上面的 W-TinyLFU 的原理描述可知,Caffeine 综合了 LFU 和 LRU 的优势,将不同特性的缓存项存入不同的缓存区域,最近刚产生的缓存项进入 Window Cache 区,不会被淘汰;访问频率高的缓存项进入 Protected Cache 区,也不会淘汰;介于这两者之间的缓存项存在 Probation 区,当缓存空间满了时,Probation 区的缓存项会根据访问频率判断是保留还是淘汰。
-
通过这种机制,很好的平衡了访问频率和访问时间新鲜程度两个维度因素,尽量将新鲜的缓存项和访问频率高的缓存项都保留在缓存中。同时在维护缓存项访问频率时,引入计数器饱和和衰减 (Count-Min Sketch, 下面会说) 机制,即节省了存储资源,也能较好的处理稀疏流量、短时超热点流量等传统 LRU 和 LFU 无法很好处理的场景。
3.Count-Min Sketch
W-TinyLFU
算法使用 Count-Min Sketch 算法存储访问频率,极大地节省了空间。
如果需要记录一个值,那我们需要通过多种 Hash 算法对其进行处理,然后在对应的 Hash 算法的记录中 +1。那为什么需要多种 Hash 算法呢?由于这是一个压缩算法必定会出现冲突,比如我们建立一个 byte
的数组,通过计算出每个数据的 Hash 的位置。比如张三和李四,他们两有可能 Hash 值都是相同,比如都是 1 。那 byte[1]
这个位置就会增加相应的频率,张三访问 1 万次,李四访问 1 次,那 byte[1]
这个位置就是 1万零1 ,如果取李四的访问评率的时候就会取出是 1万零1 ,但是李四命名只访问了1次啊!为了解决这个问题,所以用了多个 Hash 算法,可以理解为 long[][]
二维数组的一个概念。比如在第一个 Hash 算法中,张三和李四冲突了,但是在第二个,第三个 Hash 算法中很大的概率不冲突,比如一个算法大概有 1% 的概率冲突,那四个算法一起冲突的概率是 1% 的四次方。通过这个模式我们取李四的访问率的时候取所有算法中,李四访问最低频率的次数。所以他的名字叫 Count-Min Sketch 。
4.优点
集合了 LRU 和 LFU 的优势。
新增的缓存项写入 Window Cache ,可以解决稀疏流量的缓存加载问题;访问频率高的缓存项写入 Protected Cache,不会被淘汰。通过这个机制,平衡了访问频率和访问时间新鲜程度两个维度因素,能够尽可能地将新鲜的和访问频率高的缓存项保留在缓存中。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)