Redis的SET命令 在生产环境下发生的一次严重事故

2023-05-31 17:09:14 浏览数 (1)

今天给大家分享的是Redis基础命令set过期时间被覆盖问题。该命令可能是大家最为常见的一个命令,但有一个小细节可能很多人多都没注意到,今天就来演示总结一下。

该细节虽然看着很小,平常也很少关注到这点。但在实际的生产环境发生过一次,对于一些流量大的应用尤其需要注意。

场景演示

首先我们按照常规的操作,向Redis中插入一个值。示例代码如下:

代码语言:shell复制
127.0.0.1:6379> set demo kert ex 100
OK
127.0.0.1:6379> ttl demo
(integer) 87

上面的命令不难看出,向Redis中插入了一个string类型的值,key为demo,值为kert,并且为其设置了一个过期时间。

接下来,我们对该key进行重新设值(update操作)。

代码语言:shell复制
127.0.0.1:6379> set demo bruce
OK

看到这里,我们先思考3秒钟,该key的过期时间是多少?

  • 过期时间可能是第一次设置时间
  • 永不过期

带着这两种疑问,我们看看实际的结果。

代码语言:shell复制
127.0.0.1:6379> ttl demo
(integer) -1

实际上结果变成了-1。-1是什么意思呢,在Redis中要查看某个key的过期时间,我们可以使用ttl命令。它会返回三种可能的值:

  • 如果为 >= 0 则是该key的剩余过期时间,返回的时间是秒(s),如果想返回毫秒,可以使用pttl
  • 如果为 -1 则是该key没有设置过期时间
  • 如果为 -2 则是该key不存在,可能是本身就不存在也有可能是该key已到过期时间,被Redis标记为过期的key

通过实际的演示,我们返现使用set命令时,会覆盖原本key的过期时间,并且将该key设置为永久不失效的key。

解决方案

要保持该key的过期时间,这里有两种方案:

  • 先获取该key的过期时间,如果设置了过期时间,重新赋值之后再对其设置过期时间
  • 使用Redis6.0版本之后的新命令参数,keepttl

第一种方案

示例代码如下:

代码语言:shell复制
127.0.0.1:6379> set demo bruce ex 100
OK
127.0.0.1:6379> ttl demo
(integer) 96
127.0.0.1:6379> set demo 7small7
OK
127.0.0.1:6379> ttl demo
(integer) -1
127.0.0.1:6379> expire demo 96
(integer) 1
127.0.0.1:6379> ttl demo
(integer) 93

上述代码,大致的逻辑就是先设置一个带过期时间的key,并且在重新赋值前,获取过期时间,重新赋值之后再对key设置原来的过期时间。

该方式有2个大的问题,过期key的时间差,以及多个命令执行的原子性。

第二种方案

上述第1种的解决方案存在两个问题,接下来我们使用官方提供的命令参数。

代码语言:shell复制
127.0.0.1:6379> set demo bruce ex 100
OK
127.0.0.1:6379> ttl demo
(integer) 96
127.0.0.1:6379> set demo 1111 keepttl
OK
127.0.0.1:6379> ttl demo
(integer) 82

通过上面的操作,可以看到key的过期时间还是原有的过期时间。并且命令属于原子性操作,不用担心某个命令操作失败的问题。

最后还需要注意一点:官方原本提供了SETNX, SETEX, PSETEX, GETSET等命令,但现在官方推荐不要使用这样的命令,在将来这些命令可能会被移除,而推荐是SET命令 参数的方式来实现。

Note: Since the SET command options can replace SETNX, SETEX, PSETEX, GETSET, it is possible that in future versions of Redis these commands will be deprecated and finally removed.

0 人点赞