LRU 不大行?试试 LIRS
LIRS 是什么?
Low Inter-reference Recency Set,它和 LRU 一样,是一种内存淘汰策略。继承了 LRU 根据时间局部性预测内存冷热数据的特性,引进了 IRR(Inter-Reference Recency) 和 R (Recency),来增加对冷热数据的预测准确性,从而减少错误的数据淘汰,提高缓存命中率。
既然说到 LIRS 是继承 LRU,那就不得不聊聊 LRU 有哪些优缺点,以及顺便聊聊大佬们还有哪些改进吧。
先来说说 LRU
内存淘汰策略是为了解决内存空间有限的情况下,尽可能的避免有价值的数据被淘汰。
一般来说,有价值的数据就是我们常说的热点数据。
LRU 就是这么一种广泛使用的策略,其本质就是在内存不足的情况下,剔除掉内存中最近最少使用的数据,为新数据提供空间。
最近最少使用的数据,是根据时间局部性原理来预测的。基于此,该策略认为,在某个时刻被访问的数据,那么短时间内,可能会被再次访问,其访问的概率是大于相对更久之前的访问数据。
LRU 最大的优点在于简单,仅需要维护数据本身,而不需要维护更多的额外信息,来进行预测。
但是不需要其他信息的缺点也十分明显:
-
可能因为突然热度,或大的数据块,导致大部分数据的访问次数或热度不寻常的提高。导致这部分偶发性的大量数据替换“真实频繁访问”的数据,而驻留在内存中。
-
在文件仅比内存大一点,而循环访问文件的情况下。访问到文件末尾时,由于内存限制,会淘汰掉马上就要访问的文件开头的数据。理想情况下,非命中概率 应该接近于 缓存空间不足概率。
-
key 对比于数据库中,就是索引的概念。一般来说,索引访问的频率是远大数据的。
类比之下,缓存的 key 和 value的访问频率其实不一致。那内存持有的时间也应该不一样。这就是为什么 LIRS 栈中会存在非持久化key的原因,本质上就代表着不同持有时间。
其实基于以上的问题,也有过许多其他优秀的策略,或者改进版。
LFU
比如为了解决偶发的冷数据替换热数据的问题,LFU 追踪数据的历史信息,即访问频率,选择访问频率最低的淘汰。但是,过于关注历史信息,无法适应时间的变化。比如某个时刻过大的访问频率,将导致该数据永远无法被淘汰。这就是淘汰策略的时间适应性问题。
LRU-K
诸如 LRU 的改进版 LRU-K ,其主要目的也是为了解决 LRU 的冷热数据的问题。核心思想就是将“最近使用过 1 次”的判断标准扩展为“最近使用过 K 次”。
实现上,需要多维护 K 个队列,用来记录缓存数据的访问历史。只有当数据的访问次数达到 K 次时,再将数据放入缓存。淘汰时,会淘汰第 K 次访问时,间距当前时间最大的数据,即原先 LRU 的规则。
访问历史队列的淘汰策略,可以有不同规则,比如 LRU、FIFO。