Redis设置键的生存时间或过期时间的方法详解
目录
- 键的生存时间或过期时间
- 概述
- 注意:
- TTL和PTTL
- 设置过期时间
- 内部实现
- 保存过期时间
- 例子
- 读写键空间时的维护操作
键的生存时间或过期时间
概述
通过EXPIRE命令或者PEXIPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(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)
注意:
SETEX命令可以在设置一个字符串键的同时为键设置过期时间,因为这个命令是一个类型限定的命令(只能用于字符串键)
与EXPIRE命令和PEXPIRE命令类似,客户端可以通过EXPIREAT命令或PEXPIREAT命令,以秒或者毫秒精度给数据库中的某个键设置过期时间(expire time)过期时间是一个UNIX时间戳,当键的过期时间来临时,服务器就会自动从数据库中删除这个键
127.0.0.1:6379> SET key value OK 127.0.0.1:6379> EXPIREAT key 1710944249 (integer) 1 // 过期时间之前 127.0.0.1:6379> GET key "value" // 过期时间之后 127.0.0.1:6379> GET key (nil)
TTL和PTTL
TTL命令和PTTL命令接受一个带有生存时间或者过期时间的键,返回这个键的剩余生存时间,也就是,返回距离这个键被服务器自动删除还有多长时间
127.0.0.1:6379> SET key value OK 127.0.0.1:6379> EXPIRE key 1000 (integer) 1 127.0.0.1:6379> TTL key (integer) 996
设置过期时间
Redis有四个不同的命令可以用于设置键key的生存时间(键可以存在多久)或过期时间(键什么时候会被删除)
- 1.EXPIRE 命令用于将键key的生存时间设置为ttl秒
- 2.PEXPIRE命令用于将键key的生存时间设置为ttl毫秒
- 3.EXPIREAT 命令用于将键key的过期时间设置为timestamp所指定的秒数时间戳
- 4.PEXIREAT命令用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳
虽然有多种不同单位和不同形式的设置命令,但实际上EXPIRE、PEXIPIRE、EXPIREAT三个命令都是使用PEXIPREAT命令来实现的:无论客户端执行的是以上四个命令中的哪一个,经过转换之后,最终的执行效果都和执行PEXIPIREAT命令一样
内部实现
首先,EXPIRE命令可以转换成PEXIPIRE命令
def EXPIRE(key, ttl_in_sec): #将TTL从秒转换成毫秒 ttl_in_ms = sec_to_ms(ttl_in_ec)
接着,PEXIRE命令又可以转换成PEXIREAT命令:
def PEXIPRE(key, ttl_in_ms): # 获取以毫秒计算的当前UNIX时间戳 now_ms = get_curent_unix_timestamp_in_ms(); # 当前时间加上TTL,得出毫秒格式的键过期时间 PEXIREAT(key,now_ms+ttl_in_ms)
并且,EXPIREAT命令也可以转换成PEXIREAT命令:
def EXPIREAT (key, expire_time_in_sec): # 将过期时间从秒转换为毫秒 expire_time_in_ms = sec_to_ms(expire_time_in_sec) PEXPIREAT(key, expire_tiime_in_ms);
最终,EXPIRE、PEXPIRE和EXPIREAT三个命令都会转化成PEXIREAT命令来执行
保存过期时间
redisDb结构的expire字典保存了数据库中所有键的过期时间,我们称这个字典为过期字典:
- 1.过期字典的键是一个指针,这个指针指向键是一个指针,这个指针指向键空间的某个键对象(也即是某个数据库键)
- 2.过期字典的值是一个long long类型的整数。这个整数保存了键所指向的数据库键的过期时间——一个毫秒精度的UNIX时间戳。
typedef struct redisDb { // .. // 过期字典,保存着键的过期时间 dict *expire; // ... }redisDb;
例子
举个例子,图中的过期字典保存了两个键值对:
1.第一个键值对的键为alphabet键对象,值为1385877900000,这表示数据库键alphabet的过期时间为1385877600000(2013年12月)
2.第二个键值对的键为book键对象,值为1388556000000,这表示数据库键book的过期时间为1388556000000(2014年1月1日零时)当客户端执行PEXPIREAT命令(或者其他三个会转换成PEXIPREAT命令的命令)为一个
数据库键设置过期时间时,服务器会在数据库的过期字典中关联给定的数据库和过期时间
读写键空间时的维护操作
当使用Redis命令对数据库进行读写时,服务器不仅会对键空间执行指定的读写操作还会执行一些额外的维护操作,其中包括
- 在读取一个键之后(读操作和写操作都要对键进行读取),服务器会根据键是否存在来更新服务器的键空间命中(hit)次数或键空间不命中(miss)次数,这两个值可以在INFO stats命令的keyspace_hits属性和keyspace_misses属性中查看
127.0.0.1:6379> info stats # Stats total_connections_received:15 total_commands_processed:1887 instantaneous_ops_per_sec:0 total_net_input_bytes:9624 total_net_output_bytes:2803 instantaneous_input_kbps:0.00 instantaneous_output_kbps:0.00 rejected_connections:0 sync_full:0 sync_partial_ok:0 sync_partial_err:0 expired_keys:3 evicted_keys:0 keyspace_hits:38 keyspace_misses:11 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:9925 migrate_cached_sockets:0 127.0.0.1:6379>
- 在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用OBJECT idle time
key
命令可以查看键的闲置时间。
127.0.0.1:6379> set k v OK 127.0.0.1:6379> OBJECT idletime k (integer) 6
- 如果服务器在读取一个键时发现该键已经过期,那么服务器会先删除这个过期键然后才执行余下的操作
- 如果又客户端使用WATCH命令监视了某个键,那么服务器在对被监视的键进行修改之后,会将这个键标记为脏(dirty),从而让事务程序注意到这个键已经被修改过
- 服务器每次修改一个键之后,都会对脏(dirty)键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作
- 如果服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据库通知