示例内容:用于检查主题排版,请替换为自己的文章。

从重复计算说起

同一个计算被反复调用时,保存结果可以减少重复工作。但是缓存空间有限,总要决定淘汰谁。

LRU 的策略是优先淘汰最近最久没有被访问的条目。

一个最小实现

下面借助 Python 的 OrderedDict 展示顺序如何变化。这是理解策略的示例,实际并发环境还需要考虑同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from collections import OrderedDict

class LRUCache:
def __init__(self, capacity):
if capacity <= 0:
raise ValueError("capacity must be positive")
self.capacity = capacity
self.data = OrderedDict()

def get(self, key):
if key not in self.data:
raise KeyError(key)
self.data.move_to_end(key)
return self.data[key]

def put(self, key, value):
self.data[key] = value
self.data.move_to_end(key)
if len(self.data) > self.capacity:
self.data.popitem(last=False)

观察访问顺序

写入一个已存在的键,也应让它成为最近使用的条目。读取一个键时,同样更新它的位置。

1
2
3
4
5
6
cache = LRUCache(2)
cache.put("A", 1)
cache.put("B", 2)
cache.get("A")
cache.put("C", 3)
assert list(cache.data) == ["A", "C"]

此时 B 被淘汰,因为 A 刚刚被访问过。

实际使用时

对于函数调用结果缓存,可以先考虑标准库的 functools.lru_cache。自己实现策略时,还要考虑线程安全、过期时间和内存占用。

留下一个问题

如果程序不断扫描一大批只访问一次的数据,LRU 会发生什么?带着这个问题,可以继续比较其他淘汰策略。