本文源自 2010 年的一篇论文 An O(1) algorithm for implementing the LFU cache eviction scheme
简介
缓存置换算法 (Cache Eviction Algorithm) 在操作系统、数据库以及其它系统中被广泛用于缓存置换模块,当缓存空间不足时,它利用局部性原理 (Principle of Locality) 预测未来数据的使用模式,将最不可能被访问的数据清出从而提高缓存命中率。目前已经存在的缓存置换算法包括 MRU (Most Recently Used)、MFU (Most Frequently Used)、LRU (Least Recently Used) 以及 LFU (Least Frequently Used) 等。每个算法都有其各自的适用场景。到目前为止,应用范围最广的是 LRU,主要原因在于 LRU 贴近大多数应用的实际负载模式 (workloads),同时 LRU 拥有 O(1) 时间复杂度的成熟实现方案。与 LRU 类似,LFU 同样与大多数应用的负载模式相近,但目前 LFU 最佳实现方案的时间复杂度是 O(logn) ,不如 LRU。本文,我们提出一种同样达到 O(1) 时间复杂度的 LFU 实现方案,它支持的操作包括插入、访问以及删除。
目录
本文将按顺序介绍:
- LFU 的典型使用场景
- LFU 的接口说明
- 目前 LFU 的最佳实现方案
- 时间复杂度为 O(1) 的 LFU 实现方案
LFU 的典型使用场景
LFU 的一个典型使用场景就是 HTTP 的缓存代理应用 (caching network proxy application)。它位于网络服务与用户之间,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/ea51f6db7608b812c0853829564da29a.jpeg)
它通过将大多数用户可能请求的静态文件放入缓存中,来优化网络利用率,提高服务的响应速度。这种缓存代理需要满足:
- 在有限的存储资源中缓存尽可能多的、更可能被重复使用的数据
- 实现的成本应该尽可能小,保证代理在高负荷下也能正常工作
当缓存空间占满时,我们需要尽可能地将那些使用频率最低的资源清理出去 (eviction),这恰好符合 LFU 的定义。LRU 也许也是一个不错的选择,但在特定场景下,如轮流地访问 (round-robin fashion) 一连串不同的数据导致缓存不断地被置换,LRU 将可能失效。
LFU 的接口说明
type LFU interface {
// 放入键值数据
Set(k string, v interface{
})
// 用键查值
Get(k string) (v interface{
}, ok bool)
// 清出使用频率最低的键值对
EvictOne(n int)
}
目前 LFU 的最佳实现方案
在此论文发布之时,当前的 LFU 最佳实现方案满足:
- Set: O(logn)
- Get: O(logn)
- EvictOne