(五)Redis内存淘汰机制

本文介绍了Redis的过期键删除策略及内存淘汰机制。包括定时、惰性及定期删除策略的优缺点,以及六种不同的内存淘汰策略,如LRU、LFU算法的应用场景。最后讨论了如何选择合适的内存淘汰策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 Redis过期键的删除策略

  • 定时删除: 超时时间到达时,删除
  • 惰性删除: 再次访问过期数据,删除
  • 定期删除: 每隔一段周期,删除

对于定时删除:由于数据库可能同时接受成千上万的个用户的访问,那么可能有大量的key需要删除,如果我们为每一个key的超时时间都设置一个定时器,每次超时就进行删除操作,那么会导致系统的性能非常低

对于惰性删除:如果一个key长期没被访问,那么改key-value会一直存在数据库中,会一直占用内存,而redis又是一个基于内存的数据库,这样容易导致内存被耗尽

对于定期删除: Redis难以确定执行删除操作的时长和频率

因此Redis采用的是惰性删除和定期删除相结合的方式,来删除系统中的过期键

大量的key集中过期问题

定期删除执行过程中,如果突然遇到大量过期 key 的话,客户端请求必须等待定期清理过期 key 任务线程执行完成,因为这个这个定期任务线程是在 Redis 主线程中执行的。这就导致客户端请求没办法被及时处理,响应速度会比较慢。
常见的解决方案:

  1. 给key设置随机的过期时间
  2. 开启 lazy-free(惰性删除/延迟释放) 。lazy-free 特性是 Redis 4.0 开始引入的,指的是让 Redis 采用异步方式延迟释放 key 使用的内存,将该操作交给单独的子线程处理,避免阻塞主线程。

个人建议不管是否开启 lazy-free,我们都尽量给 key 设置随机过期时间。

二 Redis内存淘汰机制

内存淘汰机制:redis配置文件可以设置maxmemory,内存的最大使用量,达到限度会执行内存淘汰机制(也就是,redis用作缓存时,若内存空间用满,就会自动驱逐老的数据)

  • Redis4.0以前提供了6中数据淘汰策略:
    • volatile-lru: 从以设置了过期时间的数据集中利用 LRU 算法移除设置过期时间的 key(LRU:最近最少使用,Least Recently Used )
    • volatile-ttl: 从已设置过期时间的数据集里面选择要过期的数据淘汰
    • volatile-random: 从已设置过期时间的数据集里面任意的选择数据淘汰
    • allkeys-lru: 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key
    • allkeys-random: 当内存不足以容纳新写入数据时,从数据集中任意的淘汰数据
    • no-evictio: 禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错 (默认的淘汰策略)
  • Redis v4.0 后增加以下 2 种:
    • volatile-lfu: 从已设置过期时间的数据集中挑选最不经常使用的数据淘汰(LFU,Least Frequently Used)算法,也就是最频繁被访问的数据将来最有可能被访问到。
    • allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

LRU
LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。

Redis的LRU实现

Redis维护了一个24位时钟,可以简单理解为当前系统的时间戳,每隔一定时间会更新这个时钟。每个key对象内部同样维护了一个24位的时钟,当新增key对象的时候会把系统的时钟赋值到这个内部对象时钟。比如我现在要进行LRU,那么首先拿到当前的全局时钟,然后再找到内部时钟与全局时钟距离时间最久的(差最大)进行淘汰,这里值得注意的是全局时钟只有24位,按秒为单位来表示才能存储194天,所以可能会出现key的时钟大于全局时钟的情况,如果这种情况出现那么就两个相加而不是相减来求最久的key。
redis 根据配置的策略要么从所有的key中随机选择N个(N可以配置)要么从所有的设置了过期时间的key中选出N个键,然后再从这N个键中选出最久没有使用的一个key进行淘汰。

为什么要使用近似LRU?
由于redis可能是随记选取的N个键,然后删除N个里面最久没有使用的一个key进行淘汰。所以这只是一种近似LRU,有可能最久时间的那个键就没有被随机到。
为什么采用这种机制呢?
1、性能问题,由于近似LRU算法只是最多随机采样N个key并对其进行排序,如果精准需要对所有key进行排序,这样近似LRU性能更高

2、内存占用问题,redis对内存要求很高,会尽量降低内存使用率,如果是抽样排序可以有效降低内存的占用

3、实际效果基本相等,如果请求符合长尾法则,那么真实LRU与Redis LRU之间表现基本无差异

4、在近似情况下提供可自配置的取样率来提升精准度,例如通过 CONFIG SET maxmemory-samples 指令可以设置取样数,取样数越高越精准,如果你的CPU和内存有足够,可以提高取样数看命中率来探测最佳的采样比例。

LFU
LFU算法是Redis4.0里面新加的一种淘汰策略。它的全称是Least Frequently Used,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。

LFU是在Redis4.0后出现的,LRU的最近最少使用实际上并不精确,考虑下面的情况,两个键 A,B A 距离的时间最久 ,使用频率高,B 近,但使用频率几乎不用,如果在LRU策略删除,那么A距离的时间最久,但实际上A的使用频率要比B频繁,所以合理的淘汰策略应该是淘汰B。LFU就是为应对这种情况而生的。
LFU把原来的key对象的内部时钟的24位分成两部分,前16位还代表时钟,后8位代表一个计数器。16位的情况下如果还按照秒为单位就会导致不够用,所以一般这里以时钟为单位。而后8位表示当前key对象的访问频率,8位只能代表255,但是redis并没有采用线性上升的方式,而是通过一个复杂的公式,通过配置如下两个参数来调整数据的递增速度。

lfu-log-factor 可以调整计数器counter的增长速度,lfu-log-factor越大,counter增长的越慢。

lfu-decay-time 是一个以分钟为单位的数值,可以调整counter的减少速度。

三 如何选择淘汰策略

淘汰策略的选择可以通过下面的配置指定:

maxmemory-policy noeviction

但是这个值填什么呢?为解决这个问题,我们需要了解我们的应用请求对于Redis中存储的数据集的访问方式以及我们的诉求是什么。同时Redis也支持Runtime修改淘汰策略,这使得我们不需要重启Redis实例而实时的调整内存淘汰策略。

下面看看几种策略的适用场景:

  • allkeys-lru:如果我们的应用对缓存的访问符合幂律分布(也就是存在相对热点数据),或者我们不太清楚我们应用的缓存访问分布状况,我们可以选择allkeys-lru策略。

  • allkeys-random:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略。

  • volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction。

另外,volatile-lru策略和volatile-random策略适合我们将一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,值得一提的是将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。

redis内存淘汰机制中当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值