一、expire和ttl命令
1.1 基本用法
Redis中的EXPIER
命令可以给键值设置过期时间,相关的命令及其格式为:
1 2 |
EXPIRE key seconds PEXPIRE key milliseconds |
两个命令分别表示设置秒级和毫秒级别的过期时间,到期之后系统会自动删除该键。
EXPIRE
命令返回1
表示设置过期时间成功,返回0
表示键不存在或者设置失败。
1 2 3 4 5 6 |
127.0.0.1:6379> set k 1 OK 127.0.0.1:6379> expire k 60 (integer) 1 127.0.0.1:6379> expire x 1 (integer) 0 |
设置成功后可以使用TTL
命令查看键值的过期时间,命令返回-1
表示键不会过期,返回-2
表示该键已过期:
1 2 3 4 |
127.0.0.1:6379> ttl k (integer) 58 127.0.0.1:6379> ttl k ## 一分钟后 (integer) -2 |
对一个已经过期的键值执行
get
会返回nil。
设置过期时间成功后,如果想要取消过期时间需要使用PERSIST
命令:
1 2 3 4 5 6 7 8 |
127.0.0.1:6379> set k 1 OK 127.0.0.1:6379> expire k 60 (integer) 1 127.0.0.1:6379> persist k (integer) 1 127.0.0.1:6379> ttl k (integer) -1 |
1.2 注意事项
1. 使用set和setget命令会清除键的过期时间
对已经已经执行expire的键执行set/setget会导致expire指令失效:
1 2 3 4 5 6 |
127.0.0.1:6379> expire k 60 (integer) 1 127.0.0.1:6379> set k 2 OK 127.0.0.1:6379> ttl k (integer) -1 |
2. 使用watch监控一个拥有过期时间的键,键值过期消失将不会被认为键值改变
watch是事务中的一个命令,用来监视键值是否被修改过。如果在watch的期间内,键值过期了,将不会被发送改变通知到watch。
二、实现原理
在数据库结构中用一个字典保存了所有设置了过期时间的键值,定义在redis.h
的redisDb
结构中:
1 2 3 4 5 |
typedef struct redisDb { dict *dict; /* The keyspace for this DB */ dict *expires; /* Timeout of keys with a timeout set */ // ... } redisDb; |
每当给一个键设置过期时间后,就会在expires字典中添加一个键值对,键是设置的要过期的键,值是一个long long
类型的时间,表示什么时间会过期。
redis每隔100毫秒会扫描一次整个哈希表,把所有过期的时间删除。
三、删除策略
redis删除过期键值有三种策略:定时删除、惰性删除以及定期删除。
3.1 定时删除
根据expire设置的过期时间删除键值,设置定时器设置,不断扫描expires记录的键值,时间到了之后就删除。
这种方式对内存来说很友好,因为键值一旦过期了就被删了,不会占用额外的内存空间。但是对CPU不友好,因为需要持续扫描所有的过期元素,判断是否过期了,如果过期了就删除,没有过期继续下一次扫描。这个扫描是针对所有键值的,如果给大量的键都设置了过期时间,对CPU会造成不小的消耗。
3.2 惰性删除
惰性删除的意思是不根据过期的设置来删除键,也就是说不会扫描定时器,键过期了不立马删除,而是等到下一次get的时候删除。
这种方式对CPU友好,无需遍历所有的过期键值列表。但是对内存不友好,一旦有键值长期不使用了,会导致内存一直被占用。
3.3 定期删除
定期删除是定时删除和惰性删除的这种策略,即每个一段固定的时间才扫描一次过期列表,把过期了的键删掉。定期删除的关键点在于如何设置删除策略,多久执行一次删除,一秒钟?一分钟?或是一小时?这个要根据实际的业务情况决定。
评论