缓存淘汰策略怎选?

访客 性能优化 1

本文目录导读:

  1. 第一步:黄金法则 —— 优先考虑 LRU 或 LFU
  2. 第二步:特殊场景,考虑特殊策略
  3. 第三步:根据你用的缓存系统,直接抄作业
  4. 总结:选型流程 (决策树)
  5. 最后一道防线:业务兜底

选择缓存淘汰策略,核心在于权衡访问模式资源成本,没有绝对的“最佳策略”,只有“最合适策略”。

下面帮你理清思路,从最常用到最特殊,逐步选出适合你的方案。


第一步:黄金法则 —— 优先考虑 LRU 或 LFU

绝大多数业务场景,在这两个策略里选就够了。

策略 全称 核心理念 适合场景 缺点
LRU Least Recently Used (最近最少使用) 淘汰最久没被访问的数据 热点数据相对集中,但会随时间变化 (如新闻、商品详情、用户会话) 偶发性的大量访问会将冷数据“挤”进缓存(缓存污染)
LFU Least Frequently Used (最不经常使用) 淘汰访问频率最低的数据 热点数据非常稳定,需要长期保留 (如系统配置、基础数据、静态资源) 缓存污染:某数据短时爆火后,长期占用缓存;② 开销大:需要维护每个key的访问次数

一句话决策:

  • 业务热点会变(如热点新闻、推荐流) → LRU
  • 业务热点几乎不变(如热门榜单、经典内容) → LFU

第二步:特殊场景,考虑特殊策略

LRU / LFU 不满足需求,再看这些针对性的策略。

策略 适用场景 说明
FIFO 对数据一致性要求极高,或缓存的是一次性数据(如验证码、临时消息) 先进先出,实现简单,但可能淘汰掉仍在高频使用的数据,不推荐用于多数业务缓存。
TTL 数据有时效性(如优惠券、秒杀活动、验证码) 每个key设置绝对过期时间,时间到了强制淘汰,这是对业务强相关的数据最直接的方式。
TTL + LRU 绝大多数缓存场景的组合策略(如Redis、Memcached的默认行为) 数据到达绝对过期时间优先淘汰,未过期的数据按LRU淘汰,这是平衡数据新鲜度与缓存命中率的高效方案。
Random 极低并发、数据量巨大访问概率均匀(如全量用户画像的特征ID) 实现成本最低,理论上比LRU、LFU更抗“热点冲击”,用在一些图计算、特征存储场景。

第三步:根据你用的缓存系统,直接抄作业

不同的系统内置了不同的策略,用法也略有不同。

Redis (最常用)

  • 推荐策略: allkeys-lruvolatile-lru
    • allkeys-lru:对所有key使用LRU。大多数业务场景首选
    • volatile-lru:只对设了TTL的key使用LRU(没设TTL的永不淘汰),适合部分key永不过期、部分key需动态淘汰的场景。
  • 其他可选: allkeys-lfu / volatile-lfu (Redis 4.0+ 引入),用在热点稳定场景。
  • 不推荐: volatile-ttl (淘汰TTL最短的,容易误伤)、allkeys-randomnoeviction (不淘汰,满了直接报错)。

配置示例 (redis.conf):

# 使用所有key的LRU策略
maxmemory-policy allkeys-lru
maxmemory 4gb  # 设置缓存上限

Memcached (经典缓存)

  • 唯一策略: LRU + TTL 的组合
  • 注意: Memcached的LRU实现为 Slab-Level LRU,当一个内存块(Slab Class)满了,才会在该块内淘汰最近最少使用的item,这可能导致热点key与冷key在同一块内时,冷key被淘汰,但其他块的内存未被充分利用

CPU Cache / 操作系统内存 (硬件层)

  • CPU L1/L2 Cache: 通常使用 伪LRU (Pseudo-LRU)LFU,因为硬件实现纯LRU开销太大。
  • OS Page Cache (内存页缓存): 遵循 近似LRU (Active/Inactive list)LFU (Linux内核最近采用的框架),系统会根据内存压力动态调整策略。

选型流程 (决策树)

  1. 你的数据是否有明确的过期时间?

    • 必须设置TTL,然后在过期的数据上,再按业务热点变化选LRU或LFU。
    • → 进入下一步。
  2. 热点数据是变化的吗?

    • 变化LRU (首选)
    • 稳定LFU (次选)
  3. 场景特殊吗?

    • 一次性数据 (验证码) → TTL 或 FIFO
    • 数据均匀访问,无热点 (全量ID) → Random
    • 对一致性、延迟要求极高 → 考虑 FIFOLRU的近似实现 (如Redis的allkeys-lru在访问压力下延迟可控)

最后一道防线:业务兜底

无论选择哪种策略,缓存淘汰都是系统层面为了不OOM(内存溢出)的被动行为永远不要依赖缓存层来保证数据一致性。

必须同步做好的事:

  • 缓存穿透保护:缓存不命中时,要防止大量请求直接打到数据库(可用布隆过滤器、互斥锁)。
  • 缓存击穿保护:一个热点key过期后,瞬间大量并发请求直接读数据库(可用互斥锁、提前预热)。
  • 监控报警:监控缓存命中率、淘汰率、内存使用率,命中率低于阈值(如90%)时,需调整策略或扩容。

最后给你的几个具体建议:

  • 绝大多数Web应用:Redis 用 allkeys-lru,配合TTL。
  • 搜索引擎/推荐系统:考虑 allkeys-lfuallkeys-lru,同时为不同类型的实体设置不同的TTL(如用户画像1小时,商品摘要1天)。
  • 数据库查询缓存:直接用 LRU + TTL,让系统自动淘汰最不常用的查询结果。
  • 分布式缓存中间件(如Caffeine、Guava Cache):它们通常内置了 带权重的LRU衰减式的LFU(如Caffeine的W-TinyLFU),能自适应波动,是比纯LRU/LFU更优的选择,推荐使用。

选择策略后,记得通过压测验证:模拟真实流量模式,观察命中率和延迟变化,再做微调,没有一劳永逸的策略,监控和调优是永恒的主题。

标签: FIFO LRU

抱歉,评论功能暂时关闭!