当前位置:首页 > 问答 > 正文

Redis里那些让键失效的命令到底有哪些,怎么用才对啊?

你问的这个问题非常实际,就是想知道在Redis里,有哪些办法能让一个键值对“消失”或者“过期”,以及怎么用才不容易出错,这事儿确实不能瞎用,用错了可能会导致数据乱套或者内存泄漏,下面我就把这几条命令和它们的使用场景、坑点给你捋清楚。

第一大类:直接删除,简单粗暴

最干脆的命令就是 DEL,这个命令没啥好说的,就是永久性地删除一个或多个键,数据立刻就没了,找不回来,用法也简单:DEL key1 key2 key3 ...,比如你有个键叫 user:1001:profile,想删了它,就执行 DEL user:1001:profile

什么时候用 DEL 最合适? 当你非常确定这个数据再也不需要了,或者在进行一些明确的清理操作时,用户主动注销了账号,那你就可以用 DEL 把他相关的数据彻底抹掉。

DEL 的坑点: 它太直接了,不管这个键现在有没有被别的客户端在访问,说删就删,如果在高并发场景下,你刚删掉,另一个程序可能正准备读取它,就会读到个 nil(空),导致程序出错,所以用 DEL 要心里有数,知道自己在干什么。

第二大类:设置过期时间,让Redis自动清理

这是Redis最核心、最常用的功能之一,它允许你给键设置一个“寿命”,时间一到,Redis会自动帮你删除它,这特别适合用来做缓存、验证码、临时会话等场景,主要命令有三个:

  1. EXPIRE key seconds:这是最基础的,给一个已经存在的键设置多少秒后过期,你设置一个验证码 SET captcha:123456 "ABCD",然后想让它5分钟后失效,就接着执行 EXPIRE captcha:123456 300
  2. PEXPIRE key milliseconds:和 EXPIRE 一模一样,只是时间单位是毫秒,用于需要更精确控制过期时间的场景。PEXPIRE captcha:123456 300000 效果和上面一样。
  3. EXPIREAT key timestampPEXPIREAT key milliseconds-timestamp:这两个命令不是设置一个“时间段”,而是设置一个具体的“时间点”(Unix时间戳)。EXPIREAT 用秒级时间戳,PEXPIREAT 用毫秒级时间戳,你想让一个键在今天中午12点整过期,可以先算出12点的Unix时间戳,EXPIREAT key 1716772800

更方便的写法: 在设置键值的同时直接设置过期时间,一步到位,这样可以避免先 SETEXPIRE 之间的时间差导致的不一致风险。

  • SETEX key seconds value:相当于 SET + EXPIRESETEX captcha:123456 300 "ABCD"
  • PSETEX key milliseconds value:毫秒版本的 SETEX
  • 在Redis 2.6.12之后,SET 命令本身也增加了过期选项SET key value EX seconds 或者 SET key value PX milliseconds,这是现在最推荐的写法,因为 SET 命令功能最全。SET captcha:123456 "ABCD" EX 300

设置过期时间的坑点:

  • 关键命令返回值的理解EXPIREPEXPIRE 这些命令执行后,会返回一个数字:1 表示成功设置了过期时间;0 表示失败,失败的主要原因是你指定的 key 根本不存在,所以你不能想当然地认为命令执行了就万事大吉,最好检查一下返回值,确保键是存在的。
  • 持久化带来的不确定性:Redis的数据可以持久化到硬盘上,如果设置了一个键5秒后过期,结果3秒时Redis重启了,重启后Redis从硬盘加载数据,它会检查键的过期时间,如果距离过期还剩2秒,那么加载后2秒这个键会失效,但如果你用了RDB持久化(快照),而键在快照之后才被设置,且在下一次快照之前就过期了,那么这个键可能永远不会被持久化,重启后就彻底没了,这个细节需要根据你的持久化策略来考量。
  • 内存淘汰策略的干扰:当Redis内存满了的时候,它会根据你配置的 maxmemory-policy 来淘汰一些键,为新的数据腾地方,即使你设置的过期时间还没到,这些键也有可能被提前删除掉,比如用了 allkeys-lru 策略,Redis会优先淘汰最久未使用的键,不管它过没过期。

第三大类:先获取再删除,保证原子性

这是一个非常有用但容易被忽略的命令:GETDEL(Redis 6.2.0版本引入),它的动作是:获取指定键的值,然后立即删除这个键,这个操作是原子性的,意味着在执行过程中不会被其他命令打断。

什么时候用 GETDEL 经典场景就是“一次性”消费,你用一个Redis的List实现一个任务队列,生产者用 LPUSH 放任务,消费者用 RPOP 取任务,但传统的 RPOP 有个问题:你取到任务后,如果处理任务的过程中程序崩溃了,这个任务就永远丢失了(因为它已经被从队列里移除了),为了解决这个问题,人们发明了更复杂的机制。

GETDEL 提供了一种更简单的“一次性令牌”解决方案,你生成一个限流令牌放在Redis里,客户端来申请令牌时,使用 GETDEL token:api,这个命令能确保:

  1. 只有能拿到值的客户端,才成功消费了这个令牌。
  2. 只要拿走了令牌,这个令牌就立刻消失,绝对不会被第二个客户端重复拿到。

这就完美实现了一个简单、安全的一次性消费机制。

GETDEL 的坑点: 最主要的就是版本要求,需要Redis 6.2.0或以上,如果你的Redis版本比较老,就用不了。

总结一下怎么用才对:

  • 想永久的、坚决的删除:用 DEL,但要小心并发问题。
  • 想让数据活一段时间后自动消失(最常见):优先使用 SET key value EX seconds 这种写法,一步到位,最安全,其次考虑 SETEX,对于已存在的键,用 EXPIRE,但别忘了检查返回值确认键存在。
  • 想实现“一次性”读取并消费:如果Redis版本够,果断用 GETDEL,这是最优雅的方式。

无论用哪种方法,都要结合你的业务逻辑、Redis的持久化配置和内存淘汰策略一起来考虑,这样才能真正用得“对”。(参考资料:Redis官方文档中对 Key expiration 以及 DEL, EXPIRE, SETEX, GETDEL 等命令的说明)

Redis里那些让键失效的命令到底有哪些,怎么用才对啊?