浅谈redis的过期时间设置和过期删除机制


Posted in MySQL onMarch 18, 2022

一:设置过期时间

redis有四种命令可以用于设置键的生存时间和过期时间:

  • EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
  • PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
  • EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
  • PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.

二:保存过期时间

那么redis里面对这些key的过期时间和生存时间的信息是怎么保存的呢??
答:在数据库结构redisDb中的expires字典中保存了数据库中所有键的过期时间,我们称expire这个字典为过期字典。
(1)过期字典是一个指针,指向键空间的某个键对象。
(2)过期字典的值是一个longlong类型的整数,这个整数保存了键所指向的数据库键的过期时间–一个毫秒级的 UNIX 时间戳。

下图是一个带过期字典的数据库例子:

浅谈redis的过期时间设置和过期删除机制

过期字典是存储在redisDb这个结构里的:

typedef struct redisDb {
    ...
    
    dict *dict;     //数据库键空间,保存着数据库中所有键值对
    dict *expires      // 过期字典,保存着键的过期时间
    ...
} redisDb;

从以上结构中可以看到expire字典(过期字典)和dict字典(数据库键空间,保存着数据库中所有键值对)是并列的,由此可见expire字典的重要性。

三:移除过期时间

PERSIST 命令可以移除一个键的过期时间:

127.0.0.1:6379> set message "hello"
OK
127.0.0.1:6379> expire message 60
(integer) 1
127.0.0.1:6379> ttl message
(integer) 54
127.0.0.1:6379> persist message
(integer) 1
127.0.0.1:6379> ttl message
(integer) -1

persist命令就是expire命令的反命令,这个函数在过期字典中查找给定的键,并从过期字典中移除。
比如在数据库当前状态(如上图所示),当给book这个key移除过期时间:

redis> persist book
(integer) 1

数据库将更新成如下状态:

浅谈redis的过期时间设置和过期删除机制

可以从图中看到,当PERSIST book命令执行之后,过期字典中的 book 键消失了。

四:计算并返回剩余生存时间

ttl命令以秒为单位返回指定键的剩余生存时间。pttl以毫秒返回。两个命令都是通过计算当前时间和过期时间的差值得到剩余生存期的。

127.0.0.1:6379> set minping shuxin
OK
127.0.0.1:6379> expire minping 60
(integer) 1
127.0.0.1:6379> ttl minping
(integer) 57
127.0.0.1:6379> ttl minping
(integer) 27
127.0.0.1:6379> pttl minping
(integer) 23839
127.0.0.1:6379>

redis源码为:

void ttlCommand(redisClient *c) {
    ttlGenericCommand(c, 0);
}
void pttlCommand(redisClient *c) {
    ttlGenericCommand(c, 1);
}
void ttlGenericCommand(redisClient *c, int output_ms) {
    long long expire, ttl = -1;
    /* 如果键不存在,返回-2 */
    if (lookupKeyRead(c->db,c->argv[1]) == NULL) {
        addReplyLongLong(c,-2);
        return;
    }
    
    /* 如果键存在*/
    /*如果没有设置生存时间,返回 -1, 否则返回实际剩余时间 */
    expire = getExpire(c->db,c->argv[1]);
    if (expire != -1) {
        /* 过期时间减去当前时间,就是键的剩余时间*/
        ttl = expire-mstime();
        if (ttl < 0) ttl = 0;
    }
    if (ttl == -1) {
        addReplyLongLong(c,-1);
    } else {
         /*将毫秒转化为秒*/
        addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));
    }
}

五:过期键的删除策略

如果一个键是过期的,那它到了过期时间之后是不是马上就从内存中被被删除呢??如果不是,那过期后到底什么时候被删除呢??

其实有三种不同的删除策略:
(1):立即删除。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。
(2):惰性删除。键过期了就过期了,不管。每次从dict字典中按key取值时,先检查此key是否已经过期,如果过期了就删除它,并返回nil,如果没过期,就返回键值。
(3):定时删除。每隔一段时间,对expires字典进行检查,删除里面的过期键。
可以看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。下面对这三种删除策略进行具体分析。

立即删除

立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对cpu是最不友好的。因为删除操作会占用cpu的时间,如果刚好碰上了cpu很忙的时候,比如正在做交集或排序等计算的时候,就会给cpu造成额外的压力。

而且目前redis事件处理器对时间事件的处理方式--无序链表,查找一个key的时间复杂度为O(n),所以并不适合用来处理大量的时间事件。

惰性删除

惰性删除是指,某个键值过期后,此键值不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点很明显:浪费内存。dict字典和expires字典都要保存这个键值的信息。

举个例子,对于一些按时间点来更新的数据,比如log日志,过期后在很长的一段时间内可能都得不到访问,这样在这段时间内就要拜拜浪费这么多内存来存log。这对于性能非常依赖于内存大小的redis来说,是比较致命的

定时删除

从上面分析来看,立即删除会短时间内占用大量cpu,惰性删除会在一段时间内浪费内存,所以定时删除是一个折中的办法。
定时删除是:每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对cpu的影响。另一方面定时删除也有效的减少了因惰性删除带来的内存浪费。

六:redis使用的策略

redis使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。

到此这篇关于浅谈redis的过期时间设置和过期删除机制的文章就介绍到这了,更多相关redis的过期时间设置和过期删除内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
Idea连接MySQL数据库出现中文乱码的问题
Apr 14 MySQL
MySQL中你可能忽略的COLLATION实例详解
May 12 MySQL
MySQL深度分页(千万级数据量如何快速分页)
Jul 25 MySQL
MySQL 数据类型详情
Nov 11 MySQL
MySQL创建定时任务
Jan 22 MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 MySQL
MySQL表锁、行锁、排它锁及共享锁的使用详解
Apr 02 MySQL
MySQL提取JSON字段数据实现查询
Apr 22 MySQL
mysql 子查询的使用
Apr 28 MySQL
MySQL的表级锁,行级锁,排它锁和共享锁
Jul 15 MySQL
SQLServer常见数学函数梳理总结
Aug 05 MySQL
MySQL 原理与优化之Limit 查询优化
Aug 14 MySQL
MySQL读取JSON转换的方式
Mar 18 #MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 #MySQL
MySQL如何快速创建800w条测试数据表
Mar 17 #MySQL
利用JuiceFS使MySQL 备份验证性能提升 10 倍
MySQL 分区表中分区键为什么必须是主键的一部分
MySQL优化及索引解析
一条 SQL 语句执行过程
You might like
如何在PHP中使用Oracle数据库(2)
2006/10/09 PHP
自动分页的不完整解决方案
2007/01/12 PHP
PHP jpgraph库的配置及生成统计图表:折线图、柱状图、饼状图
2017/05/15 PHP
thinkPHP+mysql+ajax实现的仿百度一下即时搜索效果详解
2019/07/15 PHP
Jquery Ajax学习实例5 向WebService发出请求,返回泛型集合数据的异步调用
2010/03/17 Javascript
jQuery实现的立体文字渐变效果
2010/05/17 Javascript
基于jquery的用鼠标画出可移动的div
2012/09/06 Javascript
写得不错的jquery table鼠标经过变色代码
2013/09/27 Javascript
微信小程序购物商城系统开发系列-工具篇的介绍
2016/11/21 Javascript
vue.js中Vue-router 2.0基础实践教程
2017/05/08 Javascript
彻底理解js面向对象之继承
2018/02/04 Javascript
layui 弹出层回调获取弹出层数据的例子
2019/09/02 Javascript
vuex + keep-alive实现tab标签页面缓存功能
2019/10/17 Javascript
JavaScript监听键盘事件代码实现
2020/06/03 Javascript
JavaScript的一些小技巧分享
2021/01/06 Javascript
python获取指定路径下所有指定后缀文件的方法
2015/05/26 Python
Python使用BeautifulSoup库解析HTML基本使用教程
2016/03/31 Python
python3之微信文章爬虫实例讲解
2017/07/12 Python
python入门教程 python入门神图一张
2018/03/05 Python
python threading和multiprocessing模块基本用法实例分析
2019/07/25 Python
python实现修改固定模式的字符串内容操作示例
2019/12/30 Python
Python 实现自动获取种子磁力链接方式
2020/01/16 Python
在matplotlib中改变figure的布局和大小实例
2020/04/23 Python
python百行代码自制电脑端网速悬浮窗的实现
2020/05/12 Python
python 实现PIL模块在图片画线写字
2020/05/16 Python
python中time、datetime模块的使用
2020/12/14 Python
python 邮件检测工具mmpi的使用
2021/01/04 Python
Jupyter Notebook 安装配置与使用详解
2021/01/06 Python
css3 图片圆形显示 如何CSS将正方形图片显示为圆形图片布局
2014/10/10 HTML / CSS
项目合作意向书范本
2014/04/01 职场文书
幼儿园老师新年寄语2015
2014/12/08 职场文书
2015年社区工作总结
2015/04/08 职场文书
党委工作总结2015
2015/04/27 职场文书
导游词之藏龙百瀑景区
2019/12/30 职场文书
python实战之用emoji表情生成文字
2021/05/08 Python
ROS系统将python包编译为可执行文件的简单步骤
2021/07/25 Python