4. 缓存策略选型方案:LRU、LFU、TTL、W-TinyLFU 对比
4.1 策略边界与分层
缓存策略位于缓存架构的淘汰决策层,其边界是:接收缓存未命中后的空间释放请求,输出被淘汰的键和保留的键集。策略层不关心数据来源、序列化方式或网络传输,只关注“在容量达到上限时,哪些数据应该被移除”这一核心问题。
分层位置:
接入层(客户端/API) → 缓存读写层(get/set) → 淘汰策略层(LRU / LFU / TTL / W-TinyLFU) → 存储引擎层(内存表 / 持久化)
接入层(客户端/API) → 缓存读写层(get/set) → 淘汰策略层(LRU / LFU / TTL / W-TinyLFU) → 存储引擎层(内存表 / 持久化)
4.2 组件职责与核心机制
| 策略 | 淘汰依据 | 数据结构 | 时间复杂度 | 内存开销 |
|---|---|---|---|---|
| LRU | 最近一次访问时间 | 双向链表 + 哈希表 | O(1) | 中(维护链表指针) |
| LFU | 访问频率(计数) | 哈希表 + 频率链 / 最小堆 | O(1) / O(log n) | 高(维护频率计数) |
| TTL | 过期时间(/相对) | 优先队列 / 时间轮 / 惰性删除 | O(log n) / O(1) | 低 |
| W-TinyLFU | 频率 + 衰减 + 窗口 | BloomFilter + LRU + 分段LRU | O(1) | 中(紧凑计数) |
组件职责速记:
LRU 负责“最近用过,大概率再用”;LFU 负责“高频就是热点”;TTL 负责“时间到了就失效”;W-TinyLFU 综合“近期突发 + 长期热点”,通过窗口与衰减避免频率污染。
LRU 负责“最近用过,大概率再用”;LFU 负责“高频就是热点”;TTL 负责“时间到了就失效”;W-TinyLFU 综合“近期突发 + 长期热点”,通过窗口与衰减避免频率污染。
4.3 数据流与控制流分析
读写路径(数据流)
- 读命中:返回数据 → 更新策略状态(LRU 移动到链表头;LFU 增加计数;W-TinyLFU 增加频率并调整分段)
- 读未命中:从下游加载数据 → 写入缓存 → 触发容量检查 → 若超限则执行策略淘汰
- 写操作:更新数据 → 重置 TTL / 重置频率或位置 → 容量检查 → 淘汰
淘汰决策流(控制流)
控制流的核心是“谁走谁留”的判定链路:
- 容量达到阈值(如 maxmemory / maxSize)触发淘汰;
- 策略模块读取当前所有候选键的元数据(时间戳 / 频率 / 过期时间);
- 按照策略排序规则选出“最应该淘汰”的键;
- 执行删除并更新内部数据结构;
- 如果仍有空间不足,重复 2–4 直到容量合规。
信任链路风险:LRU 在顺序扫描场景下会清空热点(“缓存污染”);LFU 在突发流量下无法快速响应新热点;TTL 依赖客户端设置合理过期时间,否则造成大量过期键堆积;W-TinyLFU 对窗口大小参数敏感,调参不当会退化为普通 LRU。
4.4 选型权衡决策
选型不能脱离业务访问特征与数据生命周期。以下决策模型将四种策略映射到典型场景:
选型对比矩阵
| 策略 | 最适合场景 | 最不适合场景 | 典型中间件 |
|---|---|---|---|
| LRU | 突发流量、新闻热榜、用户会话 | 全表扫描、频繁全量遍历 | Redis allkeys-lru、Memcached |
| LFU | 长尾热点、商品详情、稳定排行榜 | 突发秒杀、瞬时新热点 | Redis allkeys-lfu、Caffeine |
| TTL | 验证码、临时令牌、缓存刷新窗口 | 无明确过期语义、热点数据反复过期 | Redis expire、ETCD lease |
| W-TinyLFU | 混合热点、CDN边缘、微服务本地缓存 | 超低内存设备(<1MB) | Caffeine、Couchbase |
决策口诀:
访问有突发 → LRU;访问有规律 → LFU;数据有时效 → TTL;既要又要 → W-TinyLFU。
访问有突发 → LRU;访问有规律 → LFU;数据有时效 → TTL;既要又要 → W-TinyLFU。
4.5 风险点与避坑指南
- LRU 缓存污染:一次全表扫描会冲掉所有热点。解决方案:使用 LRU-K 或分段 LRU(如 W-TinyLFU 的 Segmented LRU)。
- LFU 频率老化:旧热点长期占据缓存,新数据无法晋升。解决方案:引入频率衰减因子(如每 N 分钟计数减半)。
- TTL 过期堆积:大量键在同一时刻过期导致缓存雪崩。解决方案:TTL 抖动(在基础过期时间上增加随机偏移)。
- W-TinyLFU 窗口误配:窗口太小则退化为 LFU,太大则退化为 LRU。解决方案:根据业务 QPS 分布动态调整窗口大小。
常见踩坑:线上直接切换策略(如从 LRU 切到 LFU)可能导致缓存大面积失效。必须灰度 + 预热后逐步切换,并在切换期间监控命中率和延迟。
4.6 落地实施步骤
- 访问特征采集:打点记录键的访问分布、频率衰减曲线、突发比例,持续采集 7 天以上。
- 策略仿真:用离线回放工具(如 cache-trace-sim)对比 LRU / LFU / W-TinyLFU 的命中率与驱逐次数。
- 参数配置:根据仿真结果设定容量、窗口大小、衰减系数、TTL 基准值。
以 Caffeine 为例,生产中启用 W-TinyLFU 的典型配置:
// W-TinyLFU 默认启用,无需手动指定策略
Cache<String, Data> cache = Caffeine.newBuilder()
.maximumSize(100_000) // 容量上限
.expireAfterWrite(10, TimeUnit.MINUTES) // 写后过期
.recordStats() // 开启命中率采集
.build();
- 灰度上线:先放 5% 流量验证命中率和 P99 延迟,确认无误后全量。
- 持续调优:根据监控调整 maximumSize / TTL / 窗口参数,每季度复盘一次。
4.7 验收标准
| 维度 | 验收指标 | 合格线 |
|---|---|---|
| 命中率 | 整体缓存命中率 | ≥ 95%(相对原策略不下降) |
| 延迟 | P99 读延迟 | ≤ 2ms(含策略淘汰开销) |
| 污染恢复 | 突发扫描后命中率恢复时间 | ≤ 30 秒 |
| 内存稳定性 | 内存使用波动幅度 | ≤ 10% 峰值内存 |
| 可观测性 | 策略决策次数、淘汰原因分布 | 100% 可记录与导出 |
验收 checklist:
✅ 策略与业务访问特征匹配
✅ 没有因策略切换导致缓存雪崩或大面积 miss
✅ 所有风险点已配置对应防护(衰减 / 抖动 / 分段)
✅ 灰度方案与回滚预案已就绪
✅ 策略与业务访问特征匹配
✅ 没有因策略切换导致缓存雪崩或大面积 miss
✅ 所有风险点已配置对应防护(衰减 / 抖动 / 分段)
✅ 灰度方案与回滚预案已就绪