场景再现
首先,往redis里面存一个key,
然后,设置超时时间为300s,
如下图所示
紧接着,更新name的值,
问题来了,重新设置了name的值之后,这个key的过期时间是多少呢?
A 过期时间无影响,就是剩余时间
B 过期时间重置为300s
C 清除掉超时时间,key永远有效
答案是什么呢?C
执行ttl name命令之后,可以看到返回值是-1,也就是永远有效。
答案很简单,问题的场景也很简单,我想大家都可以理解。
但是如果在一个大的项目需求里面,我们在设置key过期时间的时候,我们可能就会忽略这种超时时间被清除掉的场景,从而导致功能出错。
比方说,表里有编号1到10000的数据,有一个定时任务,每天早上6点开始,每5分钟执行一次,每次按顺序读取500条数据做相关处理。方案是使用redis记录每次读取的编号起始位置。
首先初始化key,并设置超时时间为6小时:
set max_num 1
设置超时时间一天
expire max_num 60*60*6
之后,每执行一次,更新max_num的值,
set max_num 501
set max_num 1001
。。。
直到读完这10000条数据。按照预想,第二天,继续从1开始读。
实际上,在执行set max_num 501的时候,过期时间就已经失效了,第二天会从10000开始读,并不会从1开始读。
当然,这只是某个会出错的场景。实际开发中,不知不觉可能会犯其它的错误。
补充key清楚的一些场景
如果用SET, GETSET会将key对应存储的值替换成新的,也会清除掉超时时间。
如果list结构中添加一个数据或者改变hset数据的一个字段是不会清除超时时间的。
INCR/LPUSH/HSET等命令则不会清除过期时间。
在使用INCR/LPUSH/HSET这种只是修改一个key的value,而不是覆盖整个value的命令,则不会清除key的过期时间。
如果想要通过set去覆盖值那就必须重新设置expire。
首先想到先获取过期时间,然后 set 时指定过期时间,但这样毕竟要两次请求,还有一种解决方案,使用 setrange 命令
setrange命令
Redis Setrange 命令用指定的字符串覆盖给定 key 所储存的字符串值,覆盖的位置从偏移量 offset 开始。
这个命令是覆写值,不会更改过期时间。