LRU+LFU
LRU:最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
LFU:最近最不经常使用,选择最近使用次数最少的页面予以淘汰。对于每个节点,都需要维护其使用次数count、最近使用时间time。
针对这两种算法,使用图形演示其保留数据的不同点。
如果队列使用的是LRU算法保留数据
(1)访问数据后的队列数据效果
如果现在是访问了数据3,那么队列中3数据将会在头节点的位置,如下图所示:
(2)添加数据后队列中的数据效果
现在如果有新的数据5添加到队列中,此时队列的头节点是5,尾节点上将数据4移出,如下图所示:
如果队列使用的是LFU算法保留数据
(1)访问数据后的效果
假设现在访问数据2,那么访问之后数据2的次数增加1并且更新最近使用的时间。效果如下:
(2)添加数据
假设现在添加一个数据5,那么添加数据后的效果如下:
LFU删除节点的策略是: 优先删除使用次数Count最小的那个节点,因为它最近最不经常使用所以删除它。如果使用次数相同并且节点有多个,那么在这些节点中删除最近使用时间time最早的那个节点。
对比
LRU(Least Recently Used ):淘汰最后被访问时间最久的元素。
缺点:可能会由于一次冷数据的批量查询而导致大量热点的数据被删除。
LFU(Least Frequently Used):淘汰最近访问频率最小的元素。
缺点:1. 最新加入的数据常常会被踢除,因为其起始被访问次数少。 2. 如果频率时间度量是1小时,则平均一天每个小时内的访问频率1000的热点数据可能会被2个小时的一段时间内的访问频率是1001的数据剔除掉
Redis中热点数据持久保留的方案
Redis的数据是存储在内存中,这也就意味着Redis的内存是有限的。为了解决内存不足的问题,Redis提供了多种数据的淘汰策略,以下是Redis的内存淘汰策略的整理:
淘汰策略 | 描述 |
noeviction | 当内存不足时,禁止淘汰数据,写入操作报错。这是 Redis 默认的内存淘汰策略。 |
allkeys-random | 当内存不足时,从所有的 key 中,随机选出数据进行淘汰 |
volatile-random | 当内存不足时,从设置了过期时间的 key 中,随机选出数据进行淘汰 |
volatile-ttl | 当内存不足时,从设置了过期时间的 key 中,选出即将过期的数据(按照过期时间的先后,选出最先过期的数据)进行淘汰 |
allkeys-lru | 当内存不足时,从所有 key 中使用 LRU 算法,选出最近使用最少的数据进行淘汰 |
volatile-lfu | 当内存不足时,从设置了过期时间的 key 中使用 LFU 算法,选出使用频率最低的数据进行淘汰 |
allkeys-lfu | 当内存不足时,从所有 key 中使用 LFU 算法,选出使用频率最低的数据,进行淘汰 |
volatile-lru | 当内存不足时,从设置了过期时间的 key 中使用 LRU 算法,选出最近使用最少的数据进行淘汰 |
在高并发场景下缓存热点数据一般都是采用LRU或者LFU方案实现,因为其他方案对热点数据不友好。
如果采用Reids的LRU方式缓存热点数据,那么会将最近在redis中没有的数据缓存起来,如果空间的不足的情况下会移出队列中最近未访问的数据,如下图所示:
但是本方案存在一个很严重的问题,假设数据4使用了1万次,其他的数据只使用了一次,那么对应的先后使用时间上的关系如下:
此时将数据4移除就不适合了,因为它是使用次数最高的,只是最近时间没有被访问,极端的情况下可能因为大量请求来访问数据4,此时数据4在Redis中刚好被移除,那么请求将会都打到Mysql上进而导致Mysql被打垮
如果Redis中采用LFU算法缓存热点,那么内存不足的情况下会清理到最近不常用的数据,然后存储热点数据:
此方案存储热点数据比LRU方式更加合理,因为它只会清理那些不常用的数据,针对高并发下缓存大批量的热点数据建议采用这种LFU的方式。
总结:在高并发且热点数据量很大的情况下,建议使用Redis的lfu方式淘汰数据,因为此方式更加科学合理。在某些热点数据访问量一样的,那么在淘汰数据的时候依据时间来淘汰,极端情况下被清理掉的热点数据下一时刻被大量访问,此时要做一下系统的保护(最简单的是加锁访问数据库)。