LRU(最近最少使用)缓存淘汰机制
核心思想:优先淘汰最久未被访问的数据
数据结构:
- 哈希表:存储键与双向链表节点的映射,实现 O ( 1 ) O(1) O(1)查询
- 双向链表:维护数据访问时间顺序,头部为最新访问数据,尾部为最久未访问数据
操作示例:
- 访问数据时,将对应节点移到链表头部
- 插入新数据时:
- 若缓存已满,删除尾部节点
- 创建新节点插入链表头部
- 时间复杂度:所有操作 O ( 1 ) O(1) O(1)
数学表达:
设缓存容量为 C C C,当数据量达到 C + 1 C+1 C+1时,执行淘汰操作:
淘汰条件 ⇒ ∣ Cache ∣ > C \text{淘汰条件} \Rightarrow |\text{Cache}| > C 淘汰条件⇒∣Cache∣>C
LFU(最不常用)缓存淘汰机制
核心思想:优先淘汰访问频率最低的数据(频率相同时淘汰最早访问)
数据结构:
- 主哈希表:存储键与节点信息的映射
- 频率哈希表:以频率为键,存储双向链表(维护同频率数据的时序)
- 最小频率变量:记录当前最低频率
操作示例:
- 访问数据时:
- 将节点从原频率链表移除
- 频率+1后插入新频率链表头部
- 更新最小频率(若原频率链表为空)
- 插入新数据时:
- 若缓存已满,删除最小频率链表尾部节点
- 创建频率为1的新节点
- 时间复杂度:所有操作 O ( 1 ) O(1) O(1)(需精细实现)
数学表达:
设数据 d d d的访问次数为 f ( d ) f(d) f(d),淘汰优先级满足:
∀ d i ∈ Cache , f ( d 淘汰 ) ≤ f ( d i ) \forall d_i \in \text{Cache}, \quad f(d_{\text{淘汰}}) \leq f(d_i) ∀di∈Cache,f(d淘汰)≤f(di)
算法流程图
对比总结
特性 | LRU | LFU |
---|---|---|
淘汰维度 | 时间维度(最后一次访问时间) | 统计维度(历史累计访问次数) |
适用场景 | 短期热点数据 | 长期稳定热点数据 |
实现复杂度 | 较低(单链表+哈希表) | 较高(多链表+双哈希表) |
缺陷 | 对偶发批量访问敏感 | 历史高频数据可能"僵死" |
时间复杂度 | O ( 1 ) O(1) O(1) | O ( 1 ) O(1) O(1)(需特定实现) |
典型应用 | CPU缓存、浏览器缓存 | CDN缓存、推荐系统 |
Java代码实现
LRU缓存(基于哈希表 + 双向链表)
package hh.javabasicsexample.com;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author:HuangHao
* @Package:hh.javabasicsexample.com
* @Project:java-basics
* @Name:LRUCache
* @Date:2025/5/13 15:42
* @Des:支持并发访问的LRU缓存实现、使用ConcurrentHashMap存储缓存项、使用双向链表维护访问顺序、使用分段锁提高并发性能
*/
public class LRUCache {
/**
* 双向链表节点类,存储缓存的键值对
* 每个节点包含前驱和后继引用,形成双向链表结构
*/
private static class DLinkedNode {
String key; // 缓存键
String value; // 缓存值
DLinkedNode prev; // 前驱节点引用
DLinkedNode next; // 后继节点引用
public DLinkedNode() {
}
public DLinkedNode(String key, String value) {
this.key = key;
this.value = value;