为了防止 Redis 内存被越来越多的数据"撑爆",Redis 使用了惰性删除 + 定期删除的混合策略来删除过期数据,但这也并不能完全防止内存溢出。
假设,Redis 的过期策略暂未触发(可能前 1s 刚定时删除完,可能并未访问到过期的数据),但此时内存中涌进一大批数据,直接将内存占满,此时如果不进行处理的话,又将出现 OOM。
而针对上述情况,Redis 则会使用内存淘汰策略对符合条件的 key 进行删除,以此保障 Redis 的高效运行。
也就是说,Redis 为了防止内存 OOM,它会使用"主动技能"根据过期策略对过期的数据进行删除,如果"主动技能"下存在漏网之鱼,当内存占满时会触发"被动技能",根据内存淘汰策略对符合条件的数据删除。通过这样主动、被动的方案对数据进行删除,有效保证了 Redis 内存的可用性。
一、Redis 内存淘汰策略有哪些?
Redis 内存淘汰策略共有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。
1、不进行数据淘汰的策略
- noeviction(Redis3.0之后,默认的内存淘汰策略):它表示当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有新的数据写入,会报错通知禁止写入,不淘汰任何数据,但是如果没用数据写入的话,只是单纯的查询或者删除操作的话,还是可以正常工作。
2、进行数据淘汰的策略
针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。
(1)在设置了过期时间的数据中进行淘汰:
- volatile-random:随机淘汰设置了过期时间的任意键值;
- volatile-ttl:优先淘汰更早过期的键值。
- volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
- volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
(2)在所有数据范围内进行淘汰:
- allkeys-random:随机淘汰任意键值;
- allkeys-lru:淘汰整个键值中最久未使用的键值;
- allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。
二、如何选择内存淘汰策略
不同场景下,Redis 的用途不同,对数据的要求也不同,因此实际在选择内存淘汰策略时要根据具体场景选择合适的策略。
以下是腾讯针对Redis的淘汰策略设置给出的建议:
- 当 Redis 作为缓存使用的时候,推荐使用 allkeys-lru 淘汰策略。该策略会将最近最少使用的 Key 淘汰。默认情况下,使用频率最低则后期命中的概率也最低,所以将其淘汰。
- 当 Redis 作为半缓存半持久化使用时,可以使用 volatile-lru。但因为 Redis 本身不建议保存持久化数据,所以只作为备选方案。
三、LRU 算法和 LFU 算法有什么区别?
1、什么是 LRU 算法?
LRU 全称是 Least Recently Used 翻译为最近最少使用,会选择淘汰最近最少使用的数据。
传统 LRU 算法的实现是基于「链表」结构,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,当需要内存淘汰时,只需要删除链表尾部的元素即可,因为链表尾部的元素就代表最久未被使用的元素。
2、Redis 是如何实现 LRU 算法的?
如果 Redis 按照传统的方式实现 LRU 算法,一方面,Redis 需要创建额外的链表来管理缓存数据,无疑会增大其内存开销;另一方面,当数据被访问时,需要把数据移动到链表头部,如果大量数据被访问,就会出现很多节点移动操作,这样会降低 Redis 的性能。
因此,Redis 并未使用传统方式实现 LRU 算法,其实现方式是在 Redis 的对象结构体中添加一个额外的字段 lru,用于记录此数据的最后一次访问时间。
当 Redis 进行内存淘汰时,会使用随机采样的方式来淘汰数据,它是随机取 5 个值(此值可配置),然后淘汰最久没有使用的那个。
Redis 实现的 LRU 算法的优点:
- 不用为所有的数据维护一个大链表,节省了空间占用;
- 不用在每次数据访问时都移动链表项,提升了缓存的性能;
但是 LRU 算法有一个问题,无法解决缓存污染问题,比如应用一次读取了大量的数据,而这些数据只会被读取这一次,那么这些数据会留存在 Redis 缓存中很长一段时间,造成缓存污染。
3、什么是 LFU 算法?
LFU 全称是 Least Frequently Used 翻译为最近最不常用,LFU 算法是根据数据访问次数来淘汰数据的,它的核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。
所以, LFU 算法会记录每个数据的访问次数。当一个数据被再次访问时,就会增加该数据的访问次数。这样就解决了偶尔被访问一次之后,数据留存在缓存中很长一段时间的问题,相比于 LRU 算法也更合理一些。
4、Redis 是如何实现 LFU 算法的?
LFU 算法相比于 LRU 算法的实现,在 Redis 的对象结构体的 lru 字段中多记录了「数据的访问频次」的信息。
通过比较「数据的访问频次」的大小,从而决定要删除哪个数据。它的值越小表示使用频率越低,越容易淘汰,反之,值越大表示使用频率越高,越不容易淘汰。
参考:
1853

被折叠的 条评论
为什么被折叠?



