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

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

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

redis> SET key value
OK
redis> EXP 工RE key 5
(integer) 1
redis> GET key // 5 秒之内
"value"
redis> GET key // 5 秒之后
(nil)

注意
SETEX 命令可以在设直一个字符串键的同时为键设直过期时间,因为这个命令是一个类型限定的命令(只能用于字符串键),但SETEX 命令设置过期时间的原理和EXPIRE命令设置过期时间的原理是完全一样的。

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

 

redis> SET key value
OK
redis> EXPIREAT key 1377257300
(integer) 1
redis> TIME

1)"1377257296"
2)"296543"
redis> GET key // 1377257300 之前
"value"
redis> TIME
1)"1377257303"
2)"230656 "
redis> GET key // 1377257300 之后
(nil)

 

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

redis> SET key va1ue
OK
redis> EXPlRE key 1000
(integer) 1
redis> TTL key
(integer) 997
redis> SET another_key another_value
OK
redis> TIME
1) "1377333070 "
2)  "761687 "
redis> EXPlREAT another key 1377333100
(integer ) 1
redis> TTL another key
(integer) 10

设置过期时间

 

Redis 有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除) :
EXPlRE <key> <ttl> 命令用于将键key 的生存时间设置为ttl 秒。
PEXPIRE <key> <ttl> 命令用于将键key 的生存时间设置为ttl 毫秒。
EXPIREAT <key> < timestamp> 命令用于将键key 的过期时间设置为timestamp所指定的秒数时间戳。
PEXPIREAT <key> < timestamp > 命令用于将键key 的过期时间设置为timestamp所指定的毫秒数时间戳。

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

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

接着,PEXPlRE 命令又可以转换成PEXPlREAT 命令:

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

并且,EXPlREAT命令也可以转换成PEXPlREAT命令:

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

保存过期时间

redisDB结构的expires字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典:
过期字典的键是一个指针,这个指针指向键空间中的某个键对象( 也即是某个数据库键)。
过期字典的值是一个long long 类型的整数,这个整数保存了键所指向的数据库键的过期时间:一个毫秒精度的UNIX 时间戳。

typedef struct redisDb {
//...
// 过期字典,保存着键的过期时间

dict *expires ;
//...
} redisDb;

如下图展示了一个带有过期字典的数据库例子,在这个例子中,键空间保存了数据库中的所有键值对,而过期字典则保存了数据库键的过期时间。

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

图9-12 中的过期字典保存了两个键值对:
第一个键值对的键为alphabet 键对象,值为1385877600000 ,这表示数据库键alphabet 的过期时间为1385877600000 (2013年12月1日零时)。
第二个键值对的键为book键对象,值为1388556000000,这表示数据库键book的过期时间为1388556000000(2014年1月1日零时)。
当客户端执行PEXPIREAT 命令(或者其他三个会转换成PEXPIREAT命令的命令)为一个数据库键设置过期时间时,服务器会在数据库的过期字典中关联给定的数据库键和过期时间。
举个例子, 如果数据库当前的状态如图9-12 所示, 那么在服务器执行以下命令之后:

redis> PEXPIREAT message 1 391234400000
(integer) 1

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

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

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命令可以移除一个键的过期时间:

redis>PEXPlREAT message 1391234400000
(integer) 1
redis>TTL message
(integer) 13893281
redis> PERSIST message
(integer) 1
redis> TTL message
(integer) - 1

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

redis> PERSIST book
(integer) 1

数据库将更新成图9-14 所示的状态。

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

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

计算并返回剩余生存时间

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

redis> PEXPIREAT alphabet 1385877600000
(integer) 1
redis> TTL alphabet
(integer) 85 49007
redis> PTTL alphabet
(integer) 8549001011

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 和斗的情况
return ttl_in_ms
else:
#将毫秒转换为秒
return ms_to_sec(ttl_in_ms)

举个例子,对于一个过期时间为1385877600000(2013年12月1日零时)的键alphabet来说:
如果当前时间为1383282000000 (2013年11月1日零时),那么对键alphabet执行PTTL命令将返回2595600000 ,这个值是通过用a1phabet 键的过期时间减去当前时间计算得出的: 1385877600000-1383282000000=25956000000
另一方面,如果当前时间为1383282000000 ( 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:
    return 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来说:

如果当前时间为1383282000000 (2013 年II 月1 日零时),那么调用is_expired ( alphabet ) 将返回False ,因为当前时间小于alphabet 键的过期时间。
另一方面,如果当前时间为1385964000000 (2013年12月2日零时),那么调用is_expired(alphabet) 将返回True. 因为当前时间大于alphabet 键的过期时间。

注意
实现过期键判定的另一种方法是使用TTL 命令或者PTTL 命令,比如说,如采对某个键执行TTL 命令,并且命令返回的位大于等于0 ,那么说明该键未过期。在实际中, Redis检查键是否过期的方法和is_expired函数所描述的方法一致,因为直接访问字典比执行一个命令稍微快一些。

 

posted @ 2016-11-29 16:12  程序猿大哥、  阅读(355050)  评论(1编辑  收藏  举报