Redis-知识-过期时间设置及过期数据删除、内存淘汰策略

本文最后更新于:4 天前

几种过期时间设置 api

  1. EXPIRE :表示将键 key 的生存时间设置为 ttl 秒。

  2. PEXPIRE :表示将键 key 的生存时间设置为 ttl 毫秒。

  3. EXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的秒数时间戳。

  4. PEXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的毫秒数时间戳。

注: 在 Redis 内部实现中,前面三个设置过期时间的命令最后都会转换成最后一个 PEXPIREAT 命令来完成。

当我们设置一个键的过期时间时,Redis 就会将该键带上过期时间存放到一个过期字典中。

当我们查询一个键时,Redis 便首先检查该键是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。

过期删除策略

定时删除

在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。

优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。

缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。

惰性删除

设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。

优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。

缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放。从而造成内存泄漏。

定期删除

每隔一段时间,我们就对一些key进行检查,删除里面过期的key。

优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。

缺点:难以确定删除操作执行的时长和频率。

如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。

如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。

另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。

Redis 使用的删除策略

Redis 的过期删除策略是:惰性删除和定期删除两种策略配合使用。

所有键读写命令执行之前都会调用 expireIfNeeded 函数对其进行检查,如果过期,则删除该键,然后执行键不存在的操作;未过期则不作操作,继续执行原有的命令。

检查函数以一定的频率运行,每次运行时,都从一定数量的数据库中取出一定数量随机键进行检查,并删除其中的过期键。

注意:并不是一次运行就检查所有的库,所有的键,而是随机检查一定数量的键。

不同持久化方式和主从模式删除过期键流程

RDB 模式

  1. 主服务器模式运行在载入RDB文件时,程序会检查文件中的键,只会加载未过期的,过期的会被忽略,所以RDB模式下过期键不会对主服务器产生影响。

  2. 从服务器运行载入RDB文件时,会载入所有键,包括过期和未过期。当主服务器进行数据同步的时候,从服务器的数据会被清空,所以RDB文件的过期键一般不会对从服务器产生影响。

AOF 模式

AOF文件不会受过期键的影响。如果有过期键未被删除,会执行以下动作:

客户端请求过期键时:

  1. 从数据库中删除被访问的过期键;

  2. 追加一条DEL 命令到AOF文件;

  3. 向执行请求的客户端回复nil(空)

注:在执行AOF文件重写时,程序会对数据库中的键进行检查,已过期的键不会被保存到重写后的AOF文件中。

主从复制

  1. 主服务器删除过期键之后,向从服务器发送一条DEL指令,告知删除该过期键。

  2. 从服务器接收到 get 指令的时候不会对过期键进行处理,只会当做未过期键一样返回。(为了保持主从服务器数据的一致性)

  3. 从服务器只有接到主服务器发送的 DEL 指令后才会删除过期键。

内存淘汰策略 (6种)

  1. volatile-lru 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰

  2. volatile-ttl 从已设置过期时间的数据集中挑选将要过期的数据淘汰

  3. volatile-random 从已设置过期时间的数据集中任意选择数据淘汰

  4. allkeys-lru 从所有数据集中挑选最近最少使用的数据淘汰

  5. allkeys-random 从所有数据集中任意选择数据进行淘汰

  6. noeviction 禁止淘汰数据

使用场景

  • 如果不确定具体的业务特征, 那么 allkeys-lru 是一个很好的选择。因为设置 expire 会消耗额外的内存, 所以使用 allkeys-lru 策略, 可以更高效地利用内存, 因为这样就可以不再设置过期时间了。

  • 如果分为热数据与冷数据(把冷数据清除掉,保证 redis 中是热点数据),推荐使用 allkeys-lru 策略。

  • 如果需要循环读写所有的 key,或者各个 key 的访问频率差不多,可以使用 allkeys-random 策略,即读写所有元素的概率差不多。

  • 假如要让 Redis 根据 TTL 来筛选需要删除的 key,请使用 volatile-ttl 策略。

  • volatile-lru 和 volatile-random 策略主要应用场景是: 既有缓存,又有持久化的 key。一般来说,像这类场景,应该使用两个单独的 Redis 实例。