目的:用有限空间保留更可能再用的数据
LRU 不是数据库,也不是一致性协议。它只解决一个问题:缓存满了以后,优先淘汰最久没有被访问的条目。
降低慢路径成本
数据库、磁盘、网络和重复计算都可能很慢。缓存命中时直接返回,避免再次走慢路径。
利用时间局部性
刚被访问的数据,短期内更可能再次被访问。LRU 把这个经验规则工程化。
稳定淘汰规则
容量满时不随机删除,而是删除 tail 前的冷数据,行为可解释、可测试。
MRU -> LRUkey -> node*LRU Cache 解决的是一个朴素但高频的问题:空间有限时,应该保留谁、淘汰谁。 它用 HashMap 负责 O(1) 定位,用 双向链表 负责 O(1) 调整新旧顺序, 把“最近使用”变成一条可维护的时间轴。
LRU 不是数据库,也不是一致性协议。它只解决一个问题:缓存满了以后,优先淘汰最久没有被访问的条目。
数据库、磁盘、网络和重复计算都可能很慢。缓存命中时直接返回,避免再次走慢路径。
刚被访问的数据,短期内更可能再次被访问。LRU 把这个经验规则工程化。
容量满时不随机删除,而是删除 tail 前的冷数据,行为可解释、可测试。
LRU 的职责拆分很清楚:HashMap 负责 O(1) 找节点,双向链表负责 O(1) 调整新旧顺序。
避免从链表头扫到尾。命中后直接拿到节点引用。
head 后是最近使用,tail 前是最久未使用。命中和写入都移动到 head 后。
空表、单节点、删除头尾都变成同一套指针改写,减少边界分支。
查 map。未命中返回 -1;命中则移动到 head 后,再返回 value。
更新 value,并移动到 head 后。写入也算一次使用。
创建节点,加入 map,插到 head 后。若超容量,删除 tail 前节点。
map 与链表必须同步;链表顺序必须从热到冷。
公开方法只表达业务动作;摘节点、插头部、移动到头部、删除冷节点都放进 helper,代码更容易检查。
标准 LRU 的 get 不是纯读,它要移动链表节点。并发设计的关键,是在“严格顺序”和“高吞吐”之间做选择。
基础 LRU 的复杂度很漂亮,但工程版本还要考虑并发、容量、过期、一致性和观测。
get:查 map,移动节点,返回值。
put:更新或插入,必要时删 tail 前节点。
并发:严格 LRU 必须同步链表;吞吐优先时通常分片或近似。
数组移动中间元素到头部要 O(n)。LRU 每次命中都要刷新顺序,所以数组不合适。
淘汰时拿到的是 tail 前节点。节点必须带 key,才能同步删除 map 中的条目。
策略是“最近使用者留下”,机制是“map 定位 + 链表排序”。先保证不变量,再谈性能。