Redis 内存管理

参考:面试官:为什么 Redis 不立刻删除已经过期的数据?

目录

[1.Redis 给缓存数据设置过期时间有什么用?](#1.Redis 给缓存数据设置过期时间有什么用?)

[2.Redis 是如何判断数据是否过期的呢?](#2.Redis 是如何判断数据是否过期的呢?)

[3.Redis 过期 key 删除策略了解么?](#3.Redis 过期 key 删除策略了解么?)

[4.大量 key 集中过期怎么办?](#4.大量 key 集中过期怎么办?)


1.Redis 给缓存数据设置过期时间

  • 在日常中,短信验证码一般只在 1 分钟内有效,用户登录的 Token 可能只在 1 天内有效,超过过期时间就会失效。
  • 上面的例子就是Redis 给缓存数据设置过期时间的一个业务场景,那么Redis 给缓存数据设置过期时间有什么好处?
  • 因为内存是有限且珍贵的 ,如果不对缓存数据设置过期时间,那内存占用就会一直增长,最终可能会导致 OOM 问题 。通过设置合理的过期时间,Redis 会自动删除暂时不需要的数据,为新的缓存数据腾出空间。 有助于缓解内存的消耗
java 复制代码
#设置过期时间命令 expire命令,setex 命令

expire key 60   # 数据在 60s 后过期

pexpire key 60000   # 命令 pexpire 设置 key 在 60000 毫秒(即 60 秒)后过期


#设置过期时间命令 setex 命令

setex key 60 value   # 数据在 60s 后过期, seyex 将 "key" 设置为 "value",并在 60 秒后过期

psetex key 60000 value  #使用 PSETEX 指定毫秒为单位的过期时间


#查看过期时间命令ttl命令

ttl key # 查看数据还有多久过期

# 移除一个键的过期时间,使其永久存储,有效,persist 命令

persist my_key

注:Redis 中除了字符串类型 有自己独有设置过期时间的命令**setex** 外,其他方法都需要依靠**expire** 命令来设置过期时间 。


OOM 问题( Out Of Memory**)**

当 JVM 因为没有足够的内存来为对象分配空间,并且垃圾回收器也已经没有空间可回收时,就会抛出这个错误。

  1. 分配过少:JVM 初始化内存小,业务使用了大量内存;或者不同 JVM 区域分配内存不合理

  2. 代码漏洞:某一个对象被频繁申请,不用了之后却没有被释放,导致内存耗尽

内存泄漏申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了。因为申请者不用了,而又不能被虚拟机分配给别人用,久而久之内存空间会越来越小。

内存溢出申请的内存超出了 JVM 能提供的内存大小,此时称之为溢出,如果内存泄漏持续存在,最后一定会溢出,两者是因果关系。


2.Redis 判断数据是否过期

  • Redis 通过一个叫做过期字典(redisDB 结构的 expires 字典保存了数据库中所有键的过期时间, 该字典被称为过期字典,可以看作是 hash 表)来保存数据过期的时间。
  • 过期字典的键(一个指针)指向redisDB中的某个 key(键对象),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
  • 在查询一个 key 的时候,Redis 首先检查该 key 是否存在于过期字典中(时间复杂度为 O(1)),如果不在就直接返回,在的话需要判断一下这个 key 是否过期,过期直接删除 key 然后返回 null。

3.Redis 过期 key 删除策略

常用的过期数据的删除策略:

  1. 惰性删除只会在取出/查询 key 的时候才对数据进行过期检查。这种方式对 CPU 最友好 ,但是可能会造成太多过期 key 没有被删除。 好处 是:如果我们设置了过期时间的key 数量非常庞大的话,挨个遍历检查是非常耗时的,会严重影响性能。Redis 设计这种策略的目的是为了平衡内存和性能

  2. 定期删除周期性地随机从设置了过期时间的 key 中抽查一批 ,然后逐个检查这些 key 是否过期,过期就删除 key。相比于惰性删除,定期删除对内存更友好,对 CPU 不太友好。

  3. 延迟队列 :把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key 。这种方式可以保证每个过期 key 都能被删除,但维护延迟队列太麻烦,队列本身也要占用资源。 因为在key 多的情况下,一个延迟队列可能无法容纳;修改 key 的过期时间就需要调整期在延迟队列中的位置,还需要引入并发控制。

  4. 定时删除 :每个设置了过期时间的 key 都会在设置的时间到达时 立即被删除。这种方法可以确保内存中不会有过期的键,但是它对CPU 的压力最大,因为它需要为每个键都设置一个定时器。

Redis 采用的是定期删除+惰性/懒汉式删除 结合的策略

定期删除对内存更加友好,惰性删除对 CPU 更加友好,二者结合起来使用既能兼顾 CPU 友好,又能兼顾内存友好。


Redis 的定期删除过程

  1. 随机的(周期性地随机从设置了过期时间的 key 中抽查一批),并不能够保证所有过期键都会被立即删除。这就是为什么有的 key 过期了,并没有被删除。
  2. 而且Redis 底层还会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响

因此,定期删除会受到执行时间和过期 key 的比例的影响:

  • 如果删除操作的执行时间已经超过了阈值,就会中断这一次定期删除循环,以避免使用过多的 CPU 时间。

  • 如果这一批过期的 key 比例超过一个比例,就会重复执行此删除流程,以更积极地清理过期 key。相应地,如果过期的 key 比例低于这个比例,就会中断这一次定期删除循环,避免做过多的工作而获得很少的内存回收。

  • Redis 7.2 版本每次随机抽查数量是 20 ,也就是说每次会随机选择 20 个设置了过期时间的 key 判断是否过期。Redis 7.2 版本的执行时间阈值是 25ms,过期 key 比例设定值是 **10%**。

4.大量 key 集中过期

如果存在大量 key 集中过期的问题,可能会使 Redis 的请求延迟变高。

  1. 尽量避免 key 集中过期,在设置键的过期时间时尽量随机一点。

  2. 对过期的 key 开启 lazyfree 机制(修改 redis.conf 中的 lazyfree-lazy-expire参数即可),这样会在后台异步删除过期的 key,不会阻塞主线程的运行。

相关推荐
小陈phd1 分钟前
django从入门到实战(二)——FBV视图介绍
数据库·django·sqlite
架构师Wu老七13 分钟前
【软考】系统架构设计师-数据库设计基础
数据库·软考·系统架构设计师
fat house cat_13 分钟前
MySQL是怎么解决幻读和不可重复读的?
java·数据库·mysql
自由自在的小Bird28 分钟前
高频SQL50题
数据库
前端与小赵28 分钟前
什么是‌‌‌‌‌‌SQL,有什么特点
数据库·sql·oracle
转测试啦转测试啦1 小时前
Redis哨兵(sentinel)
redis·sentinel·php
farewell-Calm1 小时前
分布式多级缓存之安装和配置Canal
分布式·缓存
吹老师个人app编程教学2 小时前
ClickHouse的介绍、安装、数据类型
数据库·clickhouse·oracle
I_Am_Me_2 小时前
【MySQL】阶段性总结
数据库·oracle
网络安全queen2 小时前
网络安全等级测评师
网络·数据库·学习·安全·web安全