Code前端首页关于Code前端联系我们

民工兄Redis教程(十九):关键过期命令、注意事项及回收策略

terry 2年前 (2023-09-26) 阅读数 89 #后端开发

既然是缓存,就包括过期时间以及过期后清理回收内存的过程;本文主要讲一下redis与key过期相关的命令、注意事项以及复用策略;

注意。实际上,触发redis内存复用的情况有两种。上面是我们设置过期对象过期时的情况。有内存回收,由超时和释放触发;还有溢出回收,当内存使用达到最大内存上限时触发。

概念

Time To Live:(Time To Live,TTL),在指定的秒/毫秒后,服务器会自动删除 TTL 为 0 的 key

Expiration Time:(过期时间),时间戳,代表一个特定的时间点,在该时间点之后服务器删除密钥

相关命令

设置生存期 TTL

EXPIRE key ttl #设置ttl,单位s
PEXPIRE key ttl #设置ttl,单位ms

您可以对已经有生存期的密钥运行 EXPIRE 命令,重置生存期将代替它。老生存时间。

EXPIRATION 键秒数

设置给定键的生存时间。当密钥过期(生存时间为0)时,它会被自动删除。

在红色中,生命周期密钥称为“易失性”。

可以通过使用 DEL 命令删除整个密钥或使用 SET 和 GETSET 命令覆盖它来消除生命周期。这意味着,如果该命令仅修改(更改)具有生命周期的键的值,而不是用新的键值替换它,则生命周期不会改变。

例如,对密钥执行 INCR 命令、对列表执行 LPUSH 命令或对哈希表执行 HSET 命令不会更改密钥本身的生命周期。

但是,如果使用 RENAME 命令重命名某个键,则重命名后的键的寿命将与重命名前相同。

RENAME 命令的另一个选项是尝试将生命周期密钥重命名为具有生命周期的 another_key。此时,旧的 other_key(及其生命周期)将被删除,然后旧的 Se 密钥将重命名为 other_key。因此,新的 secondary_key 的生命周期与原始密钥相同。

使用 PERSIST 命令可以删除密钥的生命周期,而不删除该密钥,使该密钥再次永久化。

PEXPIRE key milliseconds

此命令与 EXPIRE 命令类似,但以毫秒为单位指定密钥的生命周期,而不是像 EXPIRE 命令那样以秒为单位指定。

返回值:如果设置成功,则返回1。

如果key不存在或者无法确定key的生命周期(例如在Redis 2.1之前的版本中尝试更新key的生命周期)。 3),返回0。

示例:

redis> SET cache_page "www.AA.com"
OK
 
redis> EXPIRE cache_page 30  # 设置过期时间为 30 秒
(integer) 1
 
redis> TTL cache_page    # 查看剩余生存时间
(integer) 23
 
redis> EXPIRE cache_page 30000   # 更新过期时间
(integer) 1
 
redis> TTL cache_page
(integer) 29996

更多学习Redis的文章可以在这里找到:NoSQL数据库系列-Redis。该系列不断更新。

确定过期时间(指定过期时间节点)

EXPIREAT key timestamp #设置expire time,s
PEXPIREAT key timestamp #设置exprie time,ms

密钥的时间戳 EXPIREAT

EXPIREAT 与 EXPIRE 功能类似,用于确定密钥的生存时间。

不同的是EXPIREAT命令接受的时间参数是UNIX时间戳(unix timestamp)。

PEXPIREAT-key milliseconds-timestamp

此命令与 EXPIREAT 命令类似,但以 unix 时间戳毫秒为单位指定密钥过期时间。

过期时间准确度

在Redis 2.4中,过期时间延迟在1秒之内——这意味着即使一个key过期了,它仍然可以保持在解锁过期后的一秒之内。在新的Redis 2.6版本中,延迟已降低到1毫秒以下。

上面四个命令虽然不同,但是底层都是使用PEXPIREAT实现的!

删除和更新

PERSIST key #移除生存时间

永久密钥

删除给定密钥的生命周期,将该密钥从“永久”(生命周期密钥)转换为“持久”(生命周期密钥),即永不过期的密钥)。

DLE 命令可以删除密钥并删除其生命周期。

SET 和 GETSET 命令还可以覆盖生存时间

查看剩余生存时间

TTL key #计算key的剩余生存时间,s
PTTL key #计算key的剩余生存时间,ms

TTL 密钥❀以重新确定密钥的剩余生存时间 (TTL)。

PTTL key

此命令与 TTL 命令类似,但返回密钥的剩余生命周期(以毫秒为单位)。

返回值:

  • 如果key不存在,则返回-2。
  • 如果key存在但未指定剩余生存时间,则返回-1。
  • 否则,返回密钥的剩余生命周期(以秒为单位)。

原理:如何存储过期时间

RedisDb的结果过期字典存储数据库中所有key的过期时间。 RedisDb语句如下:

/* Redis database representation. There are multiple databases identified
 * by integers from 0 (the default database) up to the max configured
 * database. The database number is the 'id' field in the structure. */
//每个数据库都是一个redisDb,id为数据库编号
typedef struct redisDb {
    dict *dict;    //键空间,保存了数据中所有键值对
    dict *expires;  //过期字典,保存了数据库中所有键的过期时间
    dict *blocking_keys;
    dict *ready_keys;
    dict *watched_keys;
    struct evictionPoolEntry *eviction_pool;
    int id;                /* Database ID */
    long long avg_ttl;      /* Average TTL, just for stats */
} redisDb;

过期键是一个指向某个键对象的指针。该值是一个很长的整数,用于保存超时。这是一个毫秒级精度的UNIX时间戳

可以看出,过期时间是使用key作为关系来存储的,所以可以通过改变key来改变过期时间。并且只有改变key的值才会改变它的过期时间;

如何计算过期时间?

底层处理方法也很简单,获取key生存时间戳并减去当前时间戳即可;

  • 如果key不存在,则返回-2;
  • 如果key没有过期时间,则返回-1;

您还可以使用此方法来检测密钥过期。如果TTL/PTTL结果小于0,则表示已经过去,如果大于0,则表示尚未过期;

Redis 中主要的弃用策略是什么

过时删除策略?

定时删除:设置定时器,在设置密钥过期时间时删除密钥

延迟删除:保留过期的密钥,每次从密钥空间获取值时检查,以决定是否删除它。 ;

定期删除:不时检查数据库并删除过期的密钥。删除多少个过期key、检查多少个数据库是由算法决定的;

各自的优缺点是什么?

定时删除

定时删除是指在设置密钥过期时间的同时创建一个定时器,以便定时器在密钥过期时立即对密钥进行删除操作。

定时删除策略是最内存友好的:通过使用定时器,定时删除策略可以保证过期的key尽快被删除,并释放过期key占用的内存。

计划删除策略的缺点是它对CPU时间最不友好:如果有很多过期的key,删除过期的key会花费大量的CPU时间。

另外,创建定时器需要使用Redis服务器上的时间事件。然而,当前时间事件的实现——查找事件的时间复杂度为 O(N) 的无序链表——无法有效地处理大量时间事件。更多学习Redis的文章请参见:NoSQL数据库系列-Redis。该系列不断更新。

拍删除

拍删除就是让钥匙过期,但是每次从钥匙室拿到钥匙时,检查一下收到的钥匙是否已经过期。如果已过期,请删除该密钥。如果未过期,则返回密钥。钥匙。

lask 删除策略对 CPU 时间最友好,但对内存最不友好。如果数据库中有很多过期的key,而这些过期的key无法被访问,它们可能永远不会被删除。ierRedis的惰性删除策略是通过函数DB.C/Expireifneded来实现的。所有Redis读写命令在运行前都会调用ExpireiFneeded函数。一段时间后,程序检查数据库并删除过期的密钥。

周期性删除策略是前两种策略的融合和折中:

  • 周期性删除策略定期删除过期的key,通过限制删除操作的时长和频率来减少删除操作。对 CPU 时间的影响。
  • 此外,定期删除策略通过定期删除过期key,有效减少过期key带来的内存浪费。

周期性删除策略的难点在于确定删除操作的持续时间和频率:

  • 如果删除操作执行过于频繁或耗时过长,周期性删除策略就会沦为定时删除策略,因此处理器花费太多时间删除过期的密钥。
  • 如果删除操作执行的次数太少或者执行时间太短,正常的删除策略与惰性删除策略相同,从而导致内存的浪费。

删除redis过期的策略?

你有没有想过一个问题?如果Redis中有很多key,我们如何高效的查找并删除过期的key呢?是否有必要检查每个键?如果同一时期有很多过期的key,Redis会继续处理过期事件,导致读写指令挂起吗?

我这里解释一下,Redis是单线程的,所以一些耗时的操作会导致Redis挂掉。例如,如果Redis数据量特别大,可以使用keys *命令列出所有key。

所有 Redis 键都可以具有过期属性,这些属性存储在过期字典内部。由于一个进程中存储了大量的key,为每个key维护精确的过期和删除机制会消耗大量的CPU,这对于单线程的Redis来说过于昂贵。

所以 - Redis 服务器实际上使用了两种策略:延迟删除和普通删除:通过同时使用这两种删除策略,服务器可以在使用合理的 CPU 时间和避免浪费内存空间之间取得平衡。

让删除:顾名思义,这是指不主动删除。只有当用户访问过期对象时才会被删除。这个方法看起来很完美。访问时检查密钥过期时间。最大的优点就是节省CPU。不需要额外的存储器或TTL链表来存储擦除信息。但如果密钥过期且长时间没有被访问,则密钥仍保留在内存中,严重消耗内存资源。

删除定时任务:为了弥补第一种方法的缺点,redis内部还维护了一个默认每秒运行10次的定时任务。计划操作中的陈旧删除逻辑使用自适应算法,并使用快和慢的速率来重用密钥。

定期删除:Redis将所有有过期时间的key放入一个字典中,然后定期随机检查字典中某些key的过期时间,并删除过期的key。

Redis 默认情况下每秒执行 10 次过期扫描:

  • 从过期字典中随机选择 20 个键
  • 从这 20 个键中删除过期键,首先重复键 25% 超过 25% pi 一步

不过,为了避免过度防止循环,Redis还设置了扫描时间的上限,默认情况下不超过25毫秒。

图示:民工哥死磕Redis教程(十九 ):Key 过期时间相关命令、注意事项、回收策略

流程描述:
  • 定时任务随机检查每个数据库空间中的20个key,当过期时删除相应的key。
  • 如果超过25%的已验证密钥过期,则循环执行回收逻辑,直到低于25%或操作超时。慢速模式的超时时间为 25 毫秒。
  • 如果之前的密钥回收逻辑过期,请在 Redis 触发内部事件之前以快速模式再次运行过期密钥回收操作。快速模式的超时时间为 1 毫秒,并且每 2 秒只能触发一次。
  • 快慢模式下内部删除逻辑相同,但执行超时时间不同。

实现定期删除策略

定期删除过期key的策略是通过函数redis.c/activeExpireCycle来实现的。当Redis服务器定期使用redis.c/serverCron函数时,会调用activeExpireCycle函数。这样在指定的时间段内多次遍历每个服务器的数据库,从数据库的过期字典中随机检查一些key的过期时间,并删除过期的key。更多学习Redis的文章请参见:NoSQL数据库系列-Redis。该系列不断更新。

关于redis超时的注意事项

DEL/SET/GETSET等命令清除超时

当使用DEL、SET、GETSET等命令覆盖key对应的值时,请使用key。具有指定的到期日期。按时间,删除对应key的过期时间。

//设置mykey的过期时间为300s
127.0.0.1:6379> set mykey hello ex 300
OK
//查看过期时间
127.0.0.1:6379> ttl mykey
(integer) 294
//使用set命令覆盖mykey的内容
127.0.0.1:6379> set mykey olleh
OK
//过期时间被清除
127.0.0.1:6379> ttl mykey
(integer) -1

INCR/LPUSH/HSET等命令不会清除过期时间

并且如果使用像INCR/LPUSH/HSET这样的命令只改变key的值而不是覆盖整个值,key将不会被清除。到期。

INCR:

//设置incr_key的过期时间为300s  
127.0.0.1:6379> set incr_key 1 ex 300  
OK  
127.0.0.1:6379> ttl incr_key  
(integer) 291  
//进行自增操作  
127.0.0.1:6379> incr incr_key  
(integer) 2  
127.0.0.1:6379> get incr_key  
"2"  
//查询过期时间,发现过期时间没有被清除  
127.0.0.1:6379> ttl incr_key  
(integer) 277

LPUSH:

//新增一个list类型的key,并添加一个为1的值  
127.0.0.1:6379> LPUSH list 1  
(integer) 1  
//为list设置300s的过期时间  
127.0.0.1:6379> expire list 300  
(integer) 1  
//查看过期时间  
127.0.0.1:6379> ttl list  
(integer) 292  
//往list里面添加值2  
127.0.0.1:6379> lpush list 2  
(integer) 2  
//查看list的所有值  
127.0.0.1:6379> lrange list 0 1  
1) "2"  
2) "1"  
//能看到往list里面添加值并没有使过期时间清除  
127.0.0.1:6379> ttl list  
(integer) 252

PERSIST 命令会清除过期时间。

如果使用 PERSIST 命令将设置了过期时间的密钥转换为持久密钥,过期时间密钥也会被删除。 。

127.0.0.1:6379> set persist_key haha ex 300  
OK  
127.0.0.1:6379> ttl persist_key  
(integer) 296  
//将key变为持久化的  
127.0.0.1:6379> persist persist_key  
(integer) 1  
//过期时间被清除  
127.0.0.1:6379> ttl persist_key  
(integer) -1

使用RENAME命令将旧密钥的过期时间转移到新密钥

例如,如果使用RENAME KEY_A KEY_B命令将KEY_A重命名为KEY_B,则无论KEY_B是否过期,新密钥KEY_B将继承KEY_A的所有属性。

//设置key_a的过期时间为300s
127.0.0.1:6379> set key_a value_a ex 300
OK
//设置key_b的过期时间为600s
127.0.0.1:6379> set key_b value_b ex 600
OK
127.0.0.1:6379> ttl key_a
(integer) 279
127.0.0.1:6379> ttl key_b
(integer) 591
//将key_a重命名为key_b
127.0.0.1:6379> rename key_a key_b
OK
//新的key_b继承了key_a的过期时间
127.0.0.1:6379> ttl key_b
(integer) 248

这里篇幅有限,就不一一列举key_a重命名为key_b的所有情况了。你可以在你的电脑上尝试一下。 key_a设置了过期时间,但是key_b没有过期时间。更多学习Redis的文章请参见:NoSQL数据库系列-Redis。该系列不断更新。

如果使用 EXPIRE/PEXPIRE 将过期时间设置为负数或 EXPIREAT/PEXPIREAT 将过期时间戳设置为经过的时间,该密钥将被删除

EXPIRE: EXPIRE命令可以更新过期时间

使用keyexpiration命令更新过期时间指定的过期时间。

//设置key_1的过期时间为100s
127.0.0.1:6379> set key_1 value_1 ex 100
OK
127.0.0.1:6379> ttl key_1
(integer) 95
更新key_1的过期时间为300s
127.0.0.1:6379> expire key_1 300
(integer) 1
127.0.0.1:6379> ttl key_1
(integer) 295

Redis 2.1.3以下版本,使用expire命令将key的过期时间更新为指定的过期时间会失败。而如果使用LPUSH/HSET等命令更改指定过期时间的key的值,Redis将会删除该key。更多学习Redis的文章请参见:NoSQL数据库系列-Redis。该系列不断更新。来源:https://blog.csdn.net/minghao0508/article/details/123895525

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门