ARC 缓存 (Adaptive Replacement)
Published: Tue Feb 17 2026 | Modified: Sat Feb 07 2026 , 4 minutes reading.
ARC 缓存:大战略家
算法背后的故事:两位顾问
想象一位国王(缓存),他的城堡空间有限。他有两位顾问:
- 实用主义者 (LRU): “留下那些最近来访的人!他们很活跃!”
- 历史学家 (LFU): “留下那些经常来访的人!他们很忠诚!”
多年来,国王们被迫只能选择一位顾问,而忽略另一位。 但在 2003 年,IBM 的研究人员提出了一套新系统:同时听取两人的意见,并惩罚那个犯错的人。
国王在城墙外维护着两个“幽灵名单”。这些名单记录了那些最近被驱逐的人的名字。
- 如果一个在 LRU 幽灵名单上的人回来了,国王意识到:“我不该听历史学家的!我需要更多空间给新近项。” -> 他扩大了 LRU 的区域。
- 如果一个在 LFU 幽灵名单上的人回来了,国王意识到:“我不该听实用主义者的!我需要更多空间给高频项。” -> 他扩大了 LFU 的区域。
这就是 ARC (自适应置换缓存)。它是一个能够实时自我调优的系统。
为什么需要它?
- 数据库服务器: 工作负载是会变的。白天,用户可能在扫描最近的记录(LRU 模式)。到了晚上,备份任务可能会反复读取相同的旧数据块(LFU 模式)。ARC 无需重启即可自动适应。
- ZFS 文件系统: ARC 因驱动了世界上最先进的文件系统之一 ZFS 而闻名。它为复杂的混合工作负载提供了极高的命中率。
算法是如何“思考”的
ARC 将缓存分为两个动态区域:
- T1 (近况): 最近只见过一次的项。
- T2 (频率): 至少见过两次的项。
以及两个幽灵区域(驱逐历史):
- B1 (近况幽灵): 从 T1 被驱逐的项。
- B2 (频率幽灵): 从 T2 被驱逐的项。
魔法逻辑:
- 设
p为 T1 的目标大小。 - 如果我们在 B1 中命中(意味着我们不该从 T1 驱逐它),我们 增加
p(给近况更多空间)。 - 如果我们在 B2 中命中(意味着我们不该从 T2 驱逐它),我们 减少
p(给频率更多空间)。
这是一个反馈循环。缓存实际上是在从它自己的驱逐错误中学习。
工程决策:专利之墙
在很长一段时间里,ARC 被 IBM 申请了专利,这阻止了许多开源项目(如 Linux 内核或 PostgreSQL)直接使用它。
- 替代方案: 这导致了 LIRS 和 Clock-Pro 的发明,它们试图在不侵犯专利的情况下逼近 ARC 的天才构想。
- 现状: 该专利大约在 2020 年过期,所以现在 ARC 可以被所有人免费使用了!
实现参考 (Python 概念版)
实现完整的 ARC 非常复杂(涉及 4 个链表!)。这里是其自适应逻辑的概念素描。
class ARCCache:
def __init__(self, capacity):
self.c = capacity
self.p = 0 # T1 (近况) 的目标大小
self.t1 = [] # 近况列表 (真实数据)
self.t2 = [] # 频率列表 (真实数据)
self.b1 = [] # 近况幽灵 (仅元数据)
self.b2 = [] # 频率幽灵 (仅元数据)
def access(self, key):
# 情况 1: 缓存在 T1 或 T2 中命中
if key in self.t1:
self.t1.remove(key)
self.t2.append(key) # 晋升到频率表
return "Hit T1"
if key in self.t2:
self.t2.remove(key)
self.t2.append(key) # 更新位置
return "Hit T2"
# 情况 2: 幽灵命中 (学习时刻)
if key in self.b1:
# 我们太早从 T1 驱逐它了!增加 T1 的大小。
delta = 1
if len(self.b1) < len(self.b2):
delta = len(self.b2) / len(self.b1)
self.p = min(self.c, self.p + delta)
self.replace(key)
# 从幽灵移回真实频率表
self.b1.remove(key)
self.t2.append(key)
return "Ghost Hit B1 - 向近况调整"
if key in self.b2:
# 我们太早从 T2 驱逐它了!减少 T1 的大小 (偏向 T2)。
delta = 1
if len(self.b2) < len(self.b1):
delta = len(self.b1) / len(self.b2)
self.p = max(0, self.p - delta)
self.replace(key)
# 从幽灵移回真实频率表
self.b2.remove(key)
self.t2.append(key)
return "Ghost Hit B2 - 向频率调整"
# 情况 3: 完全未命中
# 加入 T1 (新项)
if len(self.t1) + len(self.b1) == self.c:
if len(self.t1) < self.c:
self.b1.pop(0)
self.replace(key)
else:
self.t1.pop(0)
self.t1.append(key)
return "Miss"
def replace(self, key):
# 根据参数 'p' 决定杀掉谁
if len(self.t1) > 0 and (len(self.t1) > self.p or (key in self.b2 and len(self.t1) == self.p)):
old = self.t1.pop(0)
self.b1.append(old) # 移入近况幽灵
else:
old = self.t2.pop(0)
self.b2.append(old) # 移入频率幽灵小结
ARC 教会我们:僵化的规则在动态的世界里注定失败。通过承认自己可能会犯错并记录自己的“遗憾”(幽灵名单),ARC 达到了一种纯 LRU 或纯 LFU 都无法企及的平衡。它提醒我们,真正的智慧标志不仅在于知道,更在于学习。
