本文目录导读:
选择缓存淘汰策略,核心在于权衡访问模式与资源成本,没有绝对的“最佳策略”,只有“最合适策略”。
下面帮你理清思路,从最常用到最特殊,逐步选出适合你的方案。
第一步:黄金法则 —— 优先考虑 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-lru或volatile-lruallkeys-lru:对所有key使用LRU。大多数业务场景首选。volatile-lru:只对设了TTL的key使用LRU(没设TTL的永不淘汰),适合部分key永不过期、部分key需动态淘汰的场景。
- 其他可选:
allkeys-lfu/volatile-lfu(Redis 4.0+ 引入),用在热点稳定场景。 - 不推荐:
volatile-ttl(淘汰TTL最短的,容易误伤)、allkeys-random、noeviction(不淘汰,满了直接报错)。
配置示例 (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内核最近采用的框架),系统会根据内存压力动态调整策略。
选型流程 (决策树)
-
你的数据是否有明确的过期时间?
- 是 → 必须设置TTL,然后在过期的数据上,再按业务热点变化选LRU或LFU。
- 否 → 进入下一步。
-
热点数据是变化的吗?
- 变化 → LRU (首选)
- 稳定 → LFU (次选)
-
场景特殊吗?
- 一次性数据 (验证码) → TTL 或 FIFO
- 数据均匀访问,无热点 (全量ID) → Random
- 对一致性、延迟要求极高 → 考虑 FIFO 或 LRU的近似实现 (如Redis的
allkeys-lru在访问压力下延迟可控)
最后一道防线:业务兜底
无论选择哪种策略,缓存淘汰都是系统层面为了不OOM(内存溢出)的被动行为。永远不要依赖缓存层来保证数据一致性。
必须同步做好的事:
- 缓存穿透保护:缓存不命中时,要防止大量请求直接打到数据库(可用布隆过滤器、互斥锁)。
- 缓存击穿保护:一个热点key过期后,瞬间大量并发请求直接读数据库(可用互斥锁、提前预热)。
- 监控报警:监控缓存命中率、淘汰率、内存使用率,命中率低于阈值(如90%)时,需调整策略或扩容。
最后给你的几个具体建议:
- 绝大多数Web应用:Redis 用
allkeys-lru,配合TTL。 - 搜索引擎/推荐系统:考虑
allkeys-lfu或allkeys-lru,同时为不同类型的实体设置不同的TTL(如用户画像1小时,商品摘要1天)。 - 数据库查询缓存:直接用
LRU + TTL,让系统自动淘汰最不常用的查询结果。 - 分布式缓存中间件(如Caffeine、Guava Cache):它们通常内置了 带权重的LRU 和 衰减式的LFU(如Caffeine的W-TinyLFU),能自适应波动,是比纯LRU/LFU更优的选择,推荐使用。
选择策略后,记得通过压测验证:模拟真实流量模式,观察命中率和延迟变化,再做微调,没有一劳永逸的策略,监控和调优是永恒的主题。