日常整理:内存耗尽Redis后会发生什么

目录

内存回收

过期策略

内存淘汰策略

LRU算法

LFU算法

淘汰算法的选择


Redis,是现在被广泛使用的一个玩意,就不介绍它是什么了,也不介绍它怎么用

Redis既然作为一个组件运行在服务器上,运行在机器上,就会占用内存,我们也会给它设置内存,那,如果它的内存耗尽了会发生什么呢?

我们直接先说答案,再说后续其他的话

如果Redis内存耗尽了,达到了内存设置的上限,Redis的写命令会返回错误,但读命令可以正常返回

当然,为了避免这个问题的出现,有如下措施或者行为可以用来缓解解决

内存回收

使用Redis的时候,某些情况下,某些键值对只在特定时间有效,为了防止这些数据一直占用内存,我们可以给这些键值对设置有效期

在Redis,可以通过如下命令给某个键设置过期时间

  • expire key ttl:将 key 值的过期时间设置为 ttl秒。
  • pexpire key ttl:将 key 值的过期时间设置为ttl 毫秒。
  • expireat key timestamp:将 key 值的过期时间设置为指定的 timestamp 秒数
  • pexpireat key timestamp:将 key 值的过期时间设置为指定的 timestamp 毫秒数

另外,其实 set 命令,可以在设置key的同时也设置上过期时间,这样也可以保证设置值和设置过期时间的原子性操作

当我们设置了过期时间之后,我们可以 ttl 和 pttl 两个命令来查询剩余的过期时间,如果没有设置过期时间,这两个命令都会返回 -1 ,如果设置了一个非法的过期时间,那么,就会返回 -2

这个地方,区分一下, ttl 返回的是key剩余的秒,而 pttl 返回的是剩余的毫秒

过期策略

如果说,我们要将一个过期的键删除,我们一般是有三个策略的

  • 定时删除:我们直接就是一手,给每个键设置一个定时器,时间到了,就删掉键,这种方式对内存肯定是挺友好的,但是对CPU么,就不是很友好了,毕竟,每个定时器都会占用CPU资源
  • 懒惰删除:这玩意,就不管键有没有过期了,反正我不删,等你下次来获取的时候,我看看过期了没,过期了,我再删掉,没过期就给你返回你要的值,当然这种方式很明显对内存不是很好,都堆积在内存里
  • 定时扫描:让系统每隔一段时间,就扫描一次,找到过期的键了,就直接删掉就行,这种差不多算是上面两个方案折中的了,就是,要注意好频率,万一没扫描好,就会给人返回一个过期的键值对信息了

在Redis中,使用的是第二个和第三个,只不过说,Redis的定时扫描只会扫描设置了过期时间的key,因为,设置了过期时间的key,Redis会单独进行存储,并不会扫描所有的key,要是扫描所有的key,那也太坑了


内存淘汰策略

在Redis中,有八种内存淘汰策略,这东西可以通过参数调整配置,maxmemory-policy

也可以通过命令来动态配置 config set maxmemory-policy ${策略}

淘汰策略

说明

volatile-lru

根据 LRU 算法删除设置了过期时间的键,直到腾出可用的空间为止,如果没有可删除的键,并且内存还是不够,就报错

allkeys-lru

根据 LRU 算法删除所有的键,直到腾出可用的空间为止,如果没有可删除的键,并且内存还是不够,就报错

volatile-lfu

根据 LFU 算法删除设置了过期时间的键,直到腾出可用的空间为止,如果没有可删除的键,并且内存还是不够,就报错

allkeys-lfu

根据 LFU 算法删除所有的键,直到腾出可用的空间为止,如果没有可删除的键,且内存还是不够用时,就报错

volatile-random

随机删除设置了过期时间的键,直到腾出可用的空间为止,如果没有可删除的键,且内存还是不够用时,就报错

allkeys-random

随机删除所有键,直到腾出可用的空间为止,如果没有可删除的键,且内存还是不够用时,就报错

volatile-ttl

根据键值对象的 ttl 属性, 删除最近将要过期数据,如果没有,就报错

noeviction

默认策略,不作任何处理,直接报错

LRU算法

LRU算法的全称,叫做 Least Recently Used,也就是,最近最长时间没有被使用,这个就是针对使用时间的

传统的LRU算法,就是通过维护一个双向链表和哈希表,双向链表按照访问时间从近到远排列,而哈希表用于快速查找链表中的节点,当访问一个缓存数据的时候,先在哈希表中查找,如果存在,就从链表中删除,并将其插入到链表的头节点,表示访问过了,如果不存在,就插入到链表头节点,缓存空间不足的时候,直接淘汰掉链表的尾部数据就好

传统LRU算法是有自己的缺点的:

  1. 需要维护一个双向链表和哈希表,消耗空间
  2. 假设数据访问模式,并不符合LRU算法的假设,属于周期性访问,比如某些数据访问很频繁,但是最近不访问,那这种情况下,会导致缓存被删除

所以,redis并不会采取传统的LRU算法,而是采用了类LRU算法

redis的LRU算法核心不会差太多,也是将最近最少使用的键从内存移除出去,但是它采用了抽样的方式进行,redis会为每个键维护一个访问时间戳,当某个键被访问的时候,这个键的时间戳会被更新,在进行LRU采样的时候,redis会遍历所有的键,并抽样出一部分键,在抽样的过程中,redis会计算每个键与当前时间的差距,因此,最近访问的键更有可能被保留,长时间未被访问的键,更可能被淘汰

当然,redis的LRU抽样时,如果发现抽样的键离当前时间很近,它会重新抽样

redis会在定期执行的定时任务中,从键中选取一些键,并记录访问时间戳,这些键被访问时,会更新访问时间戳,同时,redis会检查样本池中每个键的访问时间戳,如果发现某个键的时间戳距离当前时间不足一定时间,就会将这个键从样本池中删除,并从整个键空间中重新随机选取一些键来填补样本池,以确保样本池中的键能够尽可能代表整个键空间中的键,样本池的大小可以通过 maxmemory_samples 配置项来调整的。默认情况下,样本池的大小为 5 * maxmemory,也就是最多可以选取 5 倍于内存限制的键作为样本

LFU算法

除了LRU算法之外,redis还有一个淘汰算法,那就是LFU算法,全称Least Frequently Used,最不经常使用

​​​​​​​与LRU算法不同,LFU算法根据键被访问的频率来决定淘汰哪些键

LFU算法维护了每个键被访问的频率,每次访问一个键时,该键的访问频率就会增加,当内存不足时,就会优先淘汰访问频率最低的键

LFU算法的缺点:由于LFU算法需要维护每个键的访问频率,因此需要更多的内存开销,同时在某些场景下,LFU算法也容易受到热点数据的影响

淘汰算法的选择

既然,有LRU和LFU算法两种,我们就需要选择最合适的算法,

选择LRU算法可以帮助保留最近最常使用的键值对,从而提高缓存的命中率,这可以在高并发的情况下提高Redis的性能,减少缓存的失效率

选择LFU算法可以帮助保留最常使用的键值对,从而适用于缓存访问频率相对较低的场景

如果应用程序的访问模式是较为稳定的,那么LFU算法可能更加适合。如果访问模式比较随机,那么LRU算法则可能更加适合,当然,还需要考虑Redis的硬件配置和可用内存大小

选择哪种,一定要结合业务,谨慎处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值