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

Redis过期了咋办,重新设置过期时间的方法和思路分享

当我们使用Redis时,经常会遇到一种情况:你存储在Redis里的某个数据带着过期时间,时间一到,Redis就自动把它删除了,这时候,如果程序还需要用到这个数据,却发现它已经“过期消失”了,我们该怎么办呢?核心思路很简单,重新设置”,但这简单的四个字背后,有不同的处理方法和设计思路,今天就来聊聊这些。

最直接的方法:重新设置键值对

这是最直观的想法,既然原来的键(key)因为过期被删了,那我就像第一次存数据一样,再执行一次设置命令不就行了?在命令行里,你可能会这样做:

# 假设原来的键 user:123:profile 过期了
SET user:123:profile '{"name": "张三", "age": 30}'
EXPIRE user:123:profile 3600  # 同时重新设置过期时间,比如一小时

或者在程序代码中,用你熟悉的Redis客户端库,重新调用设置值的函数,并再次附加过期时间。

这种方法的特点是:

  • 简单粗暴:逻辑清晰,容易理解和实现。
  • 适用场景:适用于那些数据可以随时被重新生成或从其他数据源(比如主数据库)重新获取的场景,用户的基本信息 profile,当缓存失效后,程序发现Redis里没有了,就去数据库里查出来,然后再塞回Redis,并设置新的过期时间。

这种方法有一个潜在问题:它把“设置值”和“设置过期时间”分成了两步(除非你用SET命令的EXPX选项一次性完成),在高并发场景下,可能会遇到一些极端情况,我们后面会提到。

Redis过期了咋办,重新设置过期时间的方法和思路分享

更优雅的方法:使用SETNX或SET的扩展参数

我们重新设置值这个操作本身可能比较“昂贵”(比如需要复杂的计算或者一次耗时的数据库查询),如果同时有大量请求发现缓存过期,都跑去数据库查询同一个数据,就会造成所谓的“缓存击穿”,给数据库带来巨大压力。

为了避免这种情况,我们可以用更聪明的方式来“重新设置”。

  1. 使用SETNX(SET if Not eXists) SETNX命令的意思是“如果这个键不存在,我才设置它”,我们可以利用这个特性来让只有一个请求去重新加载数据,思路是这样的(根据缓存更新的一般模式):

    Redis过期了咋办,重新设置过期时间的方法和思路分享

    • 当客户端A发现键key过期不存在了。
    • 客户端A立刻执行SETNX lock_key 1,尝试设置一个临时的“锁”键,这个锁键可以有一个很短的过期时间,比如5秒,防止死锁。
    • 如果SETNX返回1,说明客户端A成功拿到了锁,那么由它负责去数据库查询数据,然后写入key,并设置过期时间,完成后,删除lock_key
    • 如果SETNX返回0,说明锁已经被其他客户端(比如客户端B)拿走了,那么客户端A可以等待一小会儿,然后重新尝试从key中获取数据,此时数据很可能已经被客户端B加载进去了。

    这种方法通过引入一个简单的互斥锁,确保了只有一个请求会去执行昂贵的重建操作。

  2. 使用SET命令的NX和EX/PX参数 这是Redis 2.6.12版本之后推荐的做法,它能把设置值和设置过期时间变成一个原子操作,在重新设置值时,直接用这个命令可以保证两步操作的原子性。

    SET user:123:profile '{"name": "张三", "age": 30}' EX 3600 NX

    这里的NX表示只有键不存在时才设置,EX 3600表示过期时间为3600秒,这个命令要么全部成功(键不存在时设置了新值和过期时间),要么什么都不做(键已存在),在实现上面提到的“锁”机制时,设置锁键本身就应该用这种方式,避免设置锁和给锁设置过期时间不是原子操作的问题。

预防为主:设计合理的过期策略和更新策略

Redis过期了咋办,重新设置过期时间的方法和思路分享

与其等键过期了再手忙脚乱地重新设置,不如在系统设计之初就多想一步,这就是“思路”的部分。

  1. 差异化过期时间 给一批相关的缓存设置过期时间时,不要设置成完全一样的值,一堆商品缓存都设置成30分钟后过期,那么30分钟一到,所有商品缓存同时失效,大量请求就会同时涌向数据库,更好的做法是,在基础过期时间上加上一个随机扰动值,基础时间是30分钟,然后加上一个-5分钟到+5分钟的随机数,这样缓存就会分批失效,压力会平摊开。

  2. 主动更新(刷新) 有些非常重要的热点数据,我们可能不希望它过期后让用户感受到延迟,这时候可以采用主动更新的策略。

    • 定时任务:起一个定时任务,在数据快要过期(比如还剩三分之一过期时间)的时候,就去检查一下数据是否有更新,如果有更新,就重新从数据源拉取最新数据并刷新缓存和过期时间;如果没有更新,就简单地延长一下过期时间(使用EXPIRE命令重新设置一个完整的过期时间即可),这种做法也叫“延迟过期”。
    • 数据变更时触发:如果源数据发生了变化(比如在后台管理系统修改了用户信息),可以在修改数据库后,直接主动更新Redis中的缓存,并重置过期时间,这能保证用户总能读到最新的数据,缓存几乎不会失效。
  3. 永不过期策略? 我们也可以设置某些键永不过期(不调用EXPIRE),然后通过上面提到的“主动更新”方式来维护它,这种做法适用于数据更新不频繁,但访问量非常大的场景,缺点是如果数据在源头被修改了,需要有一套可靠的机制来保证Redis里的数据能被同步更新,否则就会导致数据不一致。

总结一下

当Redis的键过期后,重新设置它本身不难,难的是如何根据业务场景选择最合适的策略。

  • 对于不敏感、可轻易重建的数据,用最直接的重新SET方法就好。
  • 对于重建成本高、可能引起并发问题的数据,要考虑用SETNX或SET NX等命令加锁,避免“缓存击穿”。
  • 从系统设计层面,通过差异化过期时间主动更新等策略,可以很大程度上减少缓存同时失效带来的风险,提升系统的稳定性。

希望这些具体的方法和思路能对你有所帮助,没有万能药,最好的方案总是依赖于你的具体业务逻辑和性能要求。