本文来自 mp.weixin.qq.com/s/IFNAOzuPm…
1、LRU(Least Recently Used,最近最少使用)
思想:
淘汰最近最少被访问的元素。
类比:
你有一个冰箱放饮料,容量是 5 瓶。你每天可能会喝其中一些,如果冰箱满了,需要放新饮料时,就把最久没喝的那一瓶扔掉。
举例:
冰箱里有:可乐、雪碧、果汁、牛奶、咖啡。
- 你连续几天喝的是可乐、雪碧、果汁;
- 要放进新饮料“酸奶”时发现冰箱满了;
- 检查哪些最久没喝,发现咖啡最久没人动,就把咖啡扔掉。
适用场景:
数据有时间局部性,即最近用过的数据很可能还会被用到。
2、LFU(Least Frequently Used,最不常用)
思想:
淘汰被使用次数最少的元素。
类比:
冰箱每瓶饮料都记录过去一个月你喝了多少次,谁喝得最少,就优先被淘汰。
举例:
- 可乐喝了 10 次,雪碧 8 次,牛奶 1 次;
- 新饮料来了,要放进冰箱,淘汰喝得最少的牛奶。
问题:
即使牛奶最近没人喝,但因为历史上喝过几次,仍然可能保留;
而一些最近才开始变得受欢迎的饮料,可能反而被淘汰。
3、TinyLFU(轻量级 LFU)
思想:
在 LFU 的基础上,只记录近期访问频率,而且用非常节省空间的数据结构记录(如 Count-Min Sketch)。
类比:
你不可能详细记录每瓶饮料的历史喝了多少次,于是你只记录最近几天喝饮料的次数,用一个模糊但省空间的方式。
- 每次放新饮料前,先查它最近有没有被喝过;
- 如果频率很低,那就拒绝加入冰箱;
- 如果频率高,再考虑淘汰掉频率低的现有饮料。
特点:
- 占用内存少;
- 判断是否“值得”缓存,而不是盲目地加入缓存;
- 适合数据流量大、热点变化快的场景。
4、W-TinyLFU(Window-TinyLFU)
思想:
在 TinyLFU 基础上,引入一个窗口区(Window Cache) ,让新来的数据有机会“试用”,再决定是否放入正式缓存。
类比:
你在冰箱旁边放一个试饮格子,所有新饮料都会先放在试饮区:
- 如果在试饮期间被喝了,那说明这饮料可能会受欢迎,就放入正式冰箱;
- 如果没人喝,那说明没价值,直接扔掉;
- 正式冰箱还是用 TinyLFU 的频率机制来决定淘汰谁。
举例:
- 新饮料“椰子汁”先放入试饮区;
- 如果有人来喝它一次,就升到主冰箱中;
- 如果没人动它,它会被新来的饮料挤出去;
- 主冰箱内谁频率最低就被淘汰。
好处:
解决了 TinyLFU 不太友好对待“新元素”的问题;
兼顾频率与新鲜度,效果更全面。
对比
| 策略 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| LRU | 淘汰最近最少访问的元素 | 简单高效,适合时间局部性 | 无法识别高频但间隔长的访问 |
| LFU | 淘汰使用频率最低的元素 | 能保留常用数据 | 不适合频率变化快的数据 |
| TinyLFU | 用精简结构统计近期频率 | 空间省,命中率高 | 新数据可能没有加入的机会 |
| W-TinyLFU | 试用区 + TinyLFU | 兼顾新旧数据,命中率更优 | 实现更复杂,需要参数调优 |