设置键的生存时间或过期时间

通过EXPIRE或PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(Time To Live,TTL),在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键:

127.0.0.1:6379> SET key value
OK
127.0.0.1:6379> EXPIRE key 5
(integer) 1
# 5秒之内
127.0.0.1:6379> GET key
"value"
# 5秒之后
127.0.0.1:6379> GET key
(nil)

  

与EXPIRE命令和PEXPIRE命令类似,客户端可以通过EXPIREAT命令和PEXPIREAT命令,以秒或者毫秒精度给数据库中的某个键设置过期时间。过期时间是一个Unix时间戳,当键的过期时间来临时,服务器就会自动从数据库删除这个键:

127.0.0.1:6379> SET key value
OK
127.0.0.1:6379> EXPIREAT key 1538527910
(integer) 1
127.0.0.1:6379> GET key
"value"
127.0.0.1:6379> TIME
1) "1538527798"
2) "537854"
127.0.0.1:6379> TIME
1) "1538527925"
2) "431305"
127.0.0.1:6379> GET key
(nil)

  

TTL命令和PTTL命令接受个带有生存时间或过期时间的键,返回这个键的剩余生存时间,也就是,返回距离这个键被服务器自动删除还有多长时间

127.0.0.1:6379> EXPIRE key 1000
(integer) 1
127.0.0.1:6379> TTL key
(integer) 991
127.0.0.1:6379> SET another_key another_value
OK
127.0.0.1:6379> TIME
1) "1538528119"
2) "181170"
127.0.0.1:6379> EXPIREAT another_key 1538528280
(integer) 1
127.0.0.1:6379> TTL another_key
(integer) 120

  

设置过期时间

Redis有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除):

  • EXPIRE <key> <ttl>命令用于将键key的生存时间设置为ttl秒
  • PEXPIRE <key> <ttl>命令用于将键key的生存时间设置为ttl毫秒
  • EXPIREAT <key> <timestamp>命令用于将键key的过期时间设置为timestamp所指定的秒数时间戳
  • PEXPIREAT <key> <timestamp>命令用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳

虽然有多种不同单位和不同形式的设置命令,但实际上EXPIRE、PEXPIRE 、EXPIREAT三个命令都是使用PEXPIREAT 命令来实现的:无论客户端执行的是以上四种命令中的哪一个,经转换后,最终的执行效果都和执行PEXPIREAT命令一样

首先,EXPIRE命令可以转换成PEXPIRE命令:

def EXPIRE(key, ttl_in_sec):
#将TTL从秒转换成毫秒
ttl_in_ms = sec_to_ms(ttl_in_sec)
PEXPlRE(key, ttl_in_ms)

  

接着,PEXPIRE命令又可以转换成PEXPIREAT命令:

def PEXPIRE(key, ttl_in_ms):
#获取以毫秒计算的当前UNIX 时间戳
now_ms = get_current_unix_timestamp_in_ms()
#当前时间加上TTL,得出毫秒格式的键过期时间
PEXPlREAT(key, now_ms+ttl_in_ms)

  

并且,EXPIREAT命令也可以转换成PEXPIREAT命令:

def EXPIREAT (key, expire_time_in_ sec):
#将过期时间从秒转换为毫秒
expire_time_ in_ms = sec_to_ms (expire_time_in_sec)
PEXPlREAT(key, expire_time_in_ms)

  

最终,EXPIRE 、PEXPIRE 、EXPIREAT三个命令都会转换成PEXPIREAT命令来执行,如图1-1所示

图1-1   设置生存时间和设置过期时间命令之间的转换

保存过期时间

redisDb结构体的expires字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典:

  • 过期字典的键是一个指针,这个指针指向键空间中的某个键对象(也即是某个数据库键)
  • 过期字典的值是一个long long类型的整数,这个整数保存了键所指定的数据库键的过期时间——一个毫秒精度的Unix时间戳

redis.h

typedef struct redisDb {
……
//过期字典,保存着键的过期时间
dict *expires;
……
} redisDb;

  

图1-12展示了一个带有过期字典的数据库例子,在这个例子中,键空间保存了数据库中的所有键值对,而过期字典则保存了数据库键的过期时间。图1-12的键空间和过期字典中重复出现了两次alphabet键对象和book键对象。在实际中,键空间的键和过期字典的键都指向同一个键对象,所以不会出现任何重复对象,也不会浪费任何空间

图1-12   带有过期字典的数据库例子

图1-12中的过期字典保存了两个键值对:

  • 第一个键值对的键为alphabet键对象,值为1385877600000,这表示数据库键alphabet的过期时间为1385877600000(2013年12月1日零时)
  • 第二个键值对的键为book对象,值为138855600000,这表示数据库键book的过期时间为138855600000(2014年1月1日零时)

当客户端执行EXPIRE命令(或将其他三个会转换成PEXPIREAT命令的命令)为一个数据库键设置过期时间时,服务器会在数据库的过期字典中关联给定的数据库键和过期时间。举个栗子,如果数据库当前的状态如图1-12所示,那么服务器执行以下命令后

127.0.0.1:6379> PEXPIREAT message 1391324400000
(integer) 1

  

过期字典将新增一个键值对,其中键为message键对象,而值为1391324400000(2014年2月1日零时),如图1-13所示

图1-13   执行PEXPIREAT命令之后的数据库

以下是PEXPIREAT命令的伪代码定义:

def PEXPIREAT(key, expire_time_in_ms) :
#如果给定的键不存在于键空间,那么不能设置过期时间
if key not in redisDB.dict :
return 0
#在过期字典中关联键和过期时间
redisDB.expires[key]=expire_tirne_in_ms
#过期时间设置成功
return 1

   

移除过期时间

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

127.0.0.1:6379> SET message "hello world"
OK
127.0.0.1:6379> PEXPIREAT message 1538531750000
(integer) 1
127.0.0.1:6379> TTL message
(integer) 302
127.0.0.1:6379> PERSIST message
(integer) 1
127.0.0.1:6379> TTL message
(integer) -1

  

PERSIST命令就是PEXPIREAT命令的反操作:PERSIST命令在过期字典中查找给定的键,并解除键和值(过期时间)在过期字典中的关联。举个栗子,如果数据库当前的状态如图1-12所示,那么当服务器执行以下命令之后:

127.0.0.1:6379> PERSIST book
(integer) 1

  

数据库将更新成图1-14的状态

图1-14   执行PERSIST之后的数据库

可以看到,当PERSIST命令执行之后,过期字典中原来的book键值对消失了,这代表数据库键book的过期时间已被移除。以下是PERSIST命令的伪代码:

def PERSIST(key):
#如果键不存在,或者键没有设置过期时间,那么直接返回
if key not in redisDB.expires:
return 0
#移除过期字典中给定键的键值对关联
redisDB.expires.remove(key)
#键的过期时间移除成功
return 1

  

计算并返回剩余生存时间

TTL命令以秒为单位返回键的剩余生存时间,而PTTL命令而以毫秒为单位返回键的剩余生存时间

127.0.0.1:6379> PEXPIREAT alphabet 1538532350000
(integer) 1
127.0.0.1:6379> TTL alphabet
(integer) 139
127.0.0.1:6379> PTTL alphabet
(integer) 132288

  

TTL和PTTL两个命令都是通过计算键的过期时间和当前时间之间的差来实现的,以下是这两个命令的伪代码实现:

def PTTL(key):
#键不存在数据库
if key not in redisDb.dict:
return -2
#尝试取得键的过期时间
#如果键没有设置过期时间,那么expire_time_in_ms将为None
expire_time_in_ms = redisDb.expires.get(key)
#键没有设置过期时间
if expire_time_in_ms is None:
return -1
#获得当前时间
now_ms = get_current_unix_timestamp_in_ms()
#过期时间减去当前时间,得出的差就是键的剩余生存时间
return (expire_time_in_ms - now_ms) def TTL(key):
#获取以毫秒为单位的剩余生存时间
ttl_in_ms = PTTL(key)
if ttl_in_ms < 0:
#处理返回值为-2和-1的情况
return ttl_in_ms
else:
#将毫秒转换成秒
return ms_to_sec(ttl_in_ms)

  

举个栗子,对于一个过期时间为1385877600000(2013年12月1日零时)的键alphabet来说:

  • 如果当前时间为138328200000(2013年11月1日零时),那么对键alphabet执行PTTL命令返回2595600000,这个值是通过用alphabet键的过期时间减去当前时间计算得出的:1385877600000-138328200000=2595600000
  • 另一方面,如果当前时间为138328200000(2013年11月1日零时),那么对键alphabet执行TTL命令将返回2595600,这个值是通过计算alphabet键的过期时间先去当前时间的差,然后将差值从毫秒转换为秒之后得出

过期键判定

通过过期字典,程序可以用以下步骤检查一个给定键是否过期:

  1. 检查给定键是否存在于过期字典:如果存在,那么取得键的过期时间
  2. 检查当前Unix时间戳是否大于键的过期时间:如果是的话,那么键已过期,否则键未过期

可以用伪代码来描述这一过程:

def is_expired(key):
#取得键的过期时间
expire_time_in_ms = redisDb.expires.get(key)
#键没有设置过期时间
if expire_time_in_ms is None:
retrurn False
#取得当前时间的Unix时间戳
now_ms = get_current_unix_timestamp_in_ms()
#检查当前时间是否大于键的过期时间
if now_ms > expire_time_in_ms:
#键已过期
return True
else:
#键未过期
return False

  

举个栗子,对于一个过期时间为1385877600000(2013年12月1日零时)的键alphabet来说:

  • 如果当前时间为138328200000(2013年11月1日零时),那么调用is_expired(alphabet)将返回false,因为当前时间小于alphabet键的过期时间
  • 另一方面,如果当前时间为138596400000(2013年12月2日零时),那么调用is_expired(alphabet)将返回true,因为当前时间大于alphabet键的过期时间

Redis实现之数据库(二)的更多相关文章

  1. redis(二)--用Redis作MySQL数据库缓存

    用Redis作MySQL数据库缓存,必须解决2个问题.首先,应该确定用何种数据结构存储来自mysql的数据:在确定数据结构之后,还要考虑用什么标识作为该数据结构的键. 直观上看,Mysql中的数据都是 ...

  2. php版redis插件,SSDB数据库,增强型的Redis管理api实例

    php版redis插件,SSDB数据库,增强型的Redis管理api实例 SSDB是一套基于LevelDB存储引擎的非关系型数据库(NOSQL),可用于取代Redis,更适合海量数据的存储.另外,ro ...

  3. Redis学习笔记(二)-key相关命令【转载】

    转自 Redis学习笔记(二)-key相关命令 - 点解 - 博客园http://www.cnblogs.com/leny/p/5638764.html Redis支持的各种数据类型包括string, ...

  4. Redis 小白指南(二)- 基础命令和五大类型:字符串、散列、列表、集合和有序集合

    Redis 小白指南(二)- 基础命令和五大类型:字符串.散列.列表.集合和有序集合 引言 目录 基础命令 字符串类型 散列类型 列表类型 集合类型 有序集合类型 基础命令 1.获得符合规则的键名列表 ...

  5. Redis 小白指南(二)- 聊聊五大类型:字符串、散列、列表、集合和有序集合

    Redis 小白指南(二)- 聊聊五大类型:字符串.散列.列表.集合和有序集合 引言 开篇<Redis 小白指南(一)- 简介.安装.GUI 和 C# 驱动介绍>已经介绍了 Redis 的 ...

  6. Spring+Redis集成+关系型数据库持久化

    本篇文章主要介绍了"Spring+Redis集成+关系型数据库持久化",主要涉及到Spring+Redis集成+关系型数据库持久化方面的内容,对于Spring+Redis集成+关系 ...

  7. python mysql redis mongodb selneium requests二次封装为什么大都是使用类的原因,一点见解

    1.python mysql  redis mongodb selneium requests举得这5个库里面的主要被用户使用的东西全都是面向对象的,包括requests.get函数是里面每次都是实例 ...

  8. redis系列之数据库与缓存数据一致性解决方案

    redis系列之数据库与缓存数据一致性解决方案 数据库与缓存读写模式策略 写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以 ...

  9. redis之(十二)redis数据的持久化

    [一]redis的数据为什么要持久化 --->redis的存取数据性能高,是由于将所有数据都存储在内存中.当redis重启的时候,存储在内存中的数据就容易丢失. --->把redis作为数 ...

  10. Redis实现之数据库(三)

    过期键删除策略 在Redis实现之数据库(二)一小节中,我们知道了数据库键的过期时间都保存在过期字典中,又知道了如果根据过期时间去判断一个键是否过期,现在剩下的问题是:如果一个键过期了,那么它什么时候 ...

随机推荐

  1. gitk更改主题设置打不开

    ➜ project git:(master) gitk Error in startup script: unknown color name "lime" (processing ...

  2. mysql set names 命令和 mysql字符编码问题

    先看下面的执行结果: (root@localhost)[(none)]mysql>show variables like 'character%'; +--------------------- ...

  3. 从零开始的全栈工程师——js篇2.16

    js操作css样式 div.style.width=“200px” 在div标签内我们添加了一个style属性 并设定了width值 这种写法会给标签带来了大量的style属性 跟实际项目是不符的 我 ...

  4. 数据库SQL优化大总结之 百万级数据库优化方案2

      网上关于SQL优化的教程很多,但是比较杂乱.近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充. 这篇文章我花费了大量的时间查找资料.修改.排版,希望大家阅读之后, ...

  5. Android中的Matrix(矩阵)

    写在前面 看这篇笔记之前先看一下参考文章,这篇笔记没有系统的讲述矩阵和代码的东西,参考文章写的也有错误的地方,要辨证的看. 如何计算矩阵乘法 android matrix 最全方法详解与进阶(完整篇) ...

  6. Android(java)学习笔记100:使用Dexdump等工具进行反编译

    使用Dex等工具进行反编译步骤: (1)首先找到Android软件安装包中的class.dex,把APK文件改名为".zip",然后解压缩其中的class.dex文件,这是Java ...

  7. C++中的异常安全性

    http://blog.csdn.net/bonchoix/article/details/8046727 一个函数如果说是“异常安全”的,必须同时满足以下两个条件:1.不泄漏任何资源:2.不允许破坏 ...

  8. [论文理解]MetaAnchor: Learning to Detect Objects with Customized Anchors

    MetaAnchor: Learning to Detect Objects with Customized Anchors Intro 本文我其实看了几遍也没看懂,看了meta以为是一个很高大上的东 ...

  9. Jquery-EasyUI combobox下拉框使用

    制作一个json文件: <input data-options="url:'${pageContext.request.contextPath }/json/combobox_data ...

  10. Cesium专栏-测量工具测距、测面、测高(附源码下载)

    Cesium Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精 ...