Q37: 如何实现数据的缓存淘汰策略?
核心结论
缓存淘汰策略的目标,不是单纯提高命中率,而是在有限内存下优先保住最有价值的数据。
真正需要先想清楚的是:
- 缓存里放的是什么数据
- 这些数据是读热点还是写热点
- 淘汰后代价是什么
- 是否允许短时间脏读或回源抖动
脱离数据价值只谈 LRU、LFU,通常意义不大。
一、先判断哪些数据适合进入缓存
不是所有数据都值得缓存。
更适合缓存的通常是:
- 读多写少
- 回源成本高
- 访问集中
- 生命周期明确
例如:
- 配置
- 排行榜摘要
- 玩家资料摘要
- 热门公会信息
二、淘汰策略和数据类型强相关
1. LRU
适合“最近访问过的数据大概率还会继续访问”的场景。
优点是简单直观,但对周期性扫描和突发访问不一定稳定。
2. LFU
适合“长期稳定热点”的场景。
例如长期热门榜单或持续高频访问的公共数据。
但 LFU 容易让旧热点长期占位,所以常需要衰减机制。
3. TTL 驱动
适合本身就有明确生命周期的数据。
例如:
- 临时活动状态
- 会话摘要
- 限流计数
这类数据很多时候比复杂淘汰算法更适合直接按过期时间管理。
三、淘汰策略不能只靠一个全局规则
很多系统的问题在于:
- 所有缓存共用一个大池
- 所有 key 共用一套淘汰策略
这会导致低价值大对象把高价值热点挤掉。
更稳妥的方式通常是:
- 按模块分缓存域
- 为不同数据设置不同 TTL 和容量
- 对关键热点做单独保护
四、淘汰之后会发生什么更重要
缓存淘汰的真实代价在于回源。
要先判断:
- 回数据库是否会形成突刺
- 回源数据是否昂贵
- 淘汰后是否会触发大量重建
如果淘汰一批热点 key 会把数据库直接打爆,再好的命中率数字也没有意义。
五、工程上常见治理手段
1. 预热
对可预期热点,提前加载可以减少冷启动抖动。
2. 分级缓存
例如:
- 本地缓存
- Redis 缓存
- 数据库回源
3. 大 key 控制
缓存淘汰失败很多时候不是算法问题,而是大 key 把内存结构扭曲了。
4. 保护关键数据
某些核心热点不应完全交给自然淘汰,应有更稳定的容量和 TTL 策略。
六、Redis 淘汰策略只是底线,不是完整方案
Redis 的 allkeys-lru、allkeys-lfu 等策略很有用,但它们只负责在内存紧张时选择谁先被删。
真正的系统设计还要补上:
- key 设计
- TTL 设计
- 热点分层
- 回源限流
- 缓存击穿保护
七、常见误区
1. LRU 一定最好
没有通用最优,取决于访问模式。
2. 命中率高就说明缓存设计好
不一定。命中的是不是高价值请求、回源是否平稳,同样重要。
3. 淘汰策略可以事后再想
缓存一旦成规模,淘汰策略会直接影响数据库稳定性和业务体验。
参考资料
- Redis 内存淘汰策略文档
- 各类在线业务多级缓存与热点回源治理实践资料
