zoukankan      html  css  js  c++  java
  • Redis实现之数据库(二)

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

    通过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键的过期时间
  • 相关阅读:
    day38_css
    day39_css_浮动_display
    day36_html
    线段树模板2 洛谷p3373
    hdu1257 最少拦截系统
    树的重心(DFS)
    树的直径(BFS)
    面向对象复习
    面向对象练习题
    面向对象的交互
  • 原文地址:https://www.cnblogs.com/beiluowuzheng/p/9739092.html
Copyright © 2011-2022 走看看