EXPIRE 命令为指定 key 设置生存时间,当 key 过期时,它会被自动删除。
命令格式
EXPIRE key seconds
可用版本:>=1.0.0
时间复杂度:O(1)
key 生存时间的移除只能通过以下几种方式,通过 DEL 命令来删除整个 key,或者使用 PERSIST 命令可以在不删除 key 的情况下,移除 key 的生存时间,或者通过 SET、GETSET 等带有存储意义的命令来移除(覆盖)已存在 key 的生存时间。
此外,如果一个命令只是修改(alter)一个带生存时间 key 的值,而不是用一个新的 key 值来代替(replace)它的话,那么生存时间不会被改变。比如说,对一个 key 执行 INCR 命令,对一个列表进行 LPUSH 命令,或者对一个哈希表执行 HSET 命令,这类操作都不会修改 key 本身的生存时间。还有如果使用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存时间和改名前一样。RENAME 命令有一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key,这时旧的 another_key 会被删除(它的生存时间也会一同消失),然后旧的 key 会改名为 another_key,因此,新的 another_key 的生存时间也和原本的 key 一样。
可以对一个已经带有生存时间的 key 执行 EXPIRE 命令,新指定的生存时间会取代旧的生存时间。
在 Redis 2.1.3 之前的版本中,修改一个带有生存时间的 key 会导致整个 key 被删除,这一行为是受当时复制(replication)层的限制而作出的,现在这一限制已经被修复。
命令参数
- seconds:指定 key 生存的时间,单位为秒。
值得注意的是,使用非正数(负数或0)调用 EXPIRE 或 PEXPIRE 命令来设置生存时间时,会导致键 key 会被删除而不是过期。
同理,使用非正数(负数或0)调用 EXPIREAT 或 PEXPIREAT 命令来设置生存时间时,也会导致键 key 会被删除。
命令返回值
EXPIRE 命令返回有2种情况:
- 返回1,当设置成功时。
- 返回0,当指定的 key 不存在时,或不能为指定的 key 设置生存时间时(比如,在低于 2.1.3 版本的 Redis 中,尝试更新 key 的生存时间)。
过期时间的准确度
在 Redis 2.4 版本中,过期时间的延迟在1秒钟之内,即就算 key 已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到1毫秒之内。
过期与不过期
键 key 的过期信息以绝对的 Unix 时间戳形式存储(从 Redis 2.6 及更高版本开始,以毫秒为单位),这意味着 Redis 实例未启动(或未激活)时相应 key 的过期时间也在损耗(因为设置的是绝对时间戳)。
为了让过期时间机制表现良好,我们必须保证服务器时间的稳定性。如果在两台时间不一致的服务器中移动 RDB 文件时,有可能发生存在过期时间的 key 在加载时就被过期。
即使是在正在运行的 Redis 实例中,也会因为系统时间的不稳定而导致 key 的删除。比如,对一个 key 设置1000秒的过期时间,然后将 Redis 服务器所在的时间改为快2000秒,这时之前设置1000秒的 key 会立即被删除。
如何淘汰过期的 keys
Redis的 key 过期有两种方式,一是被动方式,二是主动方式。
被动方式是指当一些客户端尝试访问它时,有些 key 会被发现已经过期,这时可以触发相关 key 的过期操作。
当然,这样是不够的,因为有些过期的 key,永远不会被访问。无论如何,这些 key 应该过期,所以定时随机测试设置 key 的过期时间。最终所有这些过期的 key 将会删除,这是过期的主动方式。
主动方式具体就是 Redis 以每秒10次的频率做的事情:
- 从设置过期时间的 key 中随机抽出20个 key 进行过期检测。
- 删除所有已经过期的 key。
- 如果超过25%的 key 已经过期,重复步骤1。
这是一个简单的概率算法,基本上假设我们的样本代表整个 key 空间,我们一直进行过期 key 的检测,直到过期的 key 百分比低于25%。
这意味着,驻留在内存中的已经过期的 key 数量最大等于每秒最大写入操作量除以4。
如何在复制和 AOF 文件中处理过期
为了在不牺牲一致性的情况下获得正确的行为,当 key 到期时,在 AOF 文件中合成 DEL 操作并同步到所有的 slave 节点。
slave 节点不会自己独立的去进行过期 key(它会等待 master 节点发来的 DEL 命令),并且 slave 节点本身也完整的存储着过期的时间戳,所以当 slave 节点升级为 master 时,也可以基于自身完整的过期时间戳信息进行过期 key 的相关操作。
示例
redis> SET mykey Hello
OK
redis> EXPIRE mykey 10 # 设置过期时间10秒
(integer) 1
redis> TTL mykey
(integer) 10
redis> SET mykey World # 重新设置key,过期时间被覆盖(设置为无过期时间)
OK
redis> TTL mykey # 查看key的生存时间,显示无过期时间
(integer) -1
redis> EXPIRE mykey 0
(integer) 1
redis> GET mykey # 过期时间设置为一个非正数,相关key会被删除
(nil)