设置过期时间
Redis中一个设置过期时间的简单例子:
redis> SET key value
OK
redis> EXPIRE key 5
(integer) 1
redis> GET key // 5秒之内
"value"
redis> GET key // 5秒之后
(nil)
另一个简单的例子,通过设置具体的过期时间:
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)
Redis键的过期时间分为两种:一种是生存时间(键可以存在多久),另一种过期时间(键什么时候被删除)。时间精度也分为两种:秒和毫秒。所以Redis关于过期时间的命令有四种:
- EXPIRE命令用于将键key的生存时间设置为ttl秒。
- PEXPIRE命令用于将键key的生存时间设置为ttl毫秒。
- EXPIREAT命令用于将键key的过期时间设置为timestamp所指定的秒数时间戳。
- PEXPIREAT命令用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳。
但是,无论客户端执行的是上面的哪一个命令,都会经过转换。最终的执行结果都会和执行PEXPIREAT命令一样。
所以Redis系统是根据具体的时间戳去判断键是否过期的。
保存过期时间
在讲保存过期时间之前,我们应该知道Redis保存键值对的方式,对一个空白数据库执行以下命令:
redis> SET message "hello world"
OK
redis> RPUSH alphabet "a" "b" "c"
(integer)3
redis> HSET book name "Redis in Action"
(integer) 1
redis> HSET book author "Josiah L. Carlson"
(integer) 1
redis> HSET book publisher "Manning"
(integer) 1
此时的数据库键空间:
数据库的键空间是一个字典。键空间的键也就是数据库的键;键空间的值也就是数据库的值,这个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中的任意一种。
执行键过期命令:
redis> PEXPIREAT alphabet 1385877600000
(integer) 1
redis> PEXPIREAT book 1388556000000
(integer) 1
则此时的数据库为:
注意:实际上,为了节省空间,键空间的键和过期字典的键都指向同一个键对象。只是为了展示方便,图中才出现了两次alphabet对象和book对象。
移除过期时间
PERSIST命令可以移除一个键的过期时间:
redis> PERSIST book
(integer) 1
移除book对象过期时间之后的数据库:
实际上就是将过期字典里的特定键值对删除掉。
剩余过期时间
TTL命令以秒为单位返回键的剩余生存时间,而PTTL命令则以毫秒为单位返回键的剩余生存时间:
redis> PEXPIREAT alphabet 1385877600000
(integer) 1
redis> TTL alphabet
(integer) 8549007
redis> PTTL alphabet
(integer) 8549001011
TTL和PTTL两个命令都是通过计算键的过期时间和当前时间之间的差来实现的。
# 键不存在于数据库
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)
过期键删除
Redis服务器使用的是惰性删除和定期删除两种策略。
- 惰性删除:程序只会在取出键时才会对键进行过期检查。这种方式的好处是不会在删除其他无关的过期键上花费任何CPU的时间,对CPU时间是最友好的。缺点是对内存最不友好:如果一个键已经过期但是在以后的很长一段时间内又不会访问这个键,那这个键就不会被删除,一直留在内存中。这会导致无用的垃圾数据占用大量内存。
- 定期删除:每隔一段时间执行一次删除过期键操作。通过限制删除操作执行的时长和频率减少删除操作对CPU时间的影响,这是一个重点,我们需要根据情况合理的设置删除操作的执行时长和执行频率。
RDB
在执行SAVE命令或者BGSAVE命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件中。
在启动Redis服务器时,如果服务器开启了RDB功能,那么服务器将对RDB文件进行载入:
- 如果服务器以主服务器模式运行,那么在载入RDB文件时,程序会对文件中保存的键进行检查,如果键已经过期则不会被载入到数据库。
- 如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有键,不论是否过期,都会被载入到数据库中。不过,过期键会在主从同步的时候被删除。
AOF
当服务器以AOF持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么系统会等待它被删除,然后向AOF文件追加(append)一条DEL命令,来显式地记录该键已被删除。
主从复制模式下
当服务器运行在复制模式下时,从服务器过期键删除动作由主服务器控制。
- 主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个DEL命令,告知从服务器删除这个过期键。
- 从服务器在执行客户端发送的读命令时,即使碰到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键。
- 从服务器只有在接到主服务器发来的DEL命令之后,才会删除过期键。
也就是说从服务器不能主动删除过期键,即使此键已经过期,仍可以继续访问。直到主服务器告诉它此键已过期需要删除。