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

Redis过期失效那些事儿,聊聊怎么处理才不慌乱

综合自多位资深开发者的实践经验总结与网络技术社区讨论,如开源中国、Stack Overflow 等)

Redis 这个高性能的键值数据库,咱们很多项目都在用,尤其是它的过期键功能,用好了能自动清理不用的数据,省心省力,但有时候,它也会闹点“小脾气”,比如你以为它已经过期的数据,怎么好像还在?或者大量数据同时失效,系统突然卡顿了一下,这些问题要是没提前琢磨过,真到了线上出问题的时候,难免会手忙脚乱,今天咱就像朋友聊天一样,掰扯掰扯 Redis 过期失效里面的门道,以及怎么应对才能心里有底,不慌乱。

咱得明白 Redis 是咋让数据“过期”的,你设置一个过期时间,SET mykey "hello" EX 60,这条数据就只能活 60 秒,时间一到,Redis 就会把它删掉,腾出空间,这听起来很简单,但关键在于 Redis 并不是在键过期的那一毫秒就立刻删除它,为啥呢?想象一下,Redis 里存了百万千万个键,每个都精确到毫秒去检查删除,那 Redis 自己啥也别干了,光忙着看手表了,CPU 肯定吃不消。

那它咋办呢?Redis 用了两种主要的策略来结合着干活,这两种策略在很多技术博客里都被反复提到过,第一种叫 “惰性删除”,顾名思义,就是比较“懒”,不主动出击,当客户端尝试访问一个键的时候,Redis 才会顺便检查一下这个键是否过期了,如果过期了,当场就删除,然后告诉客户端“这个键不存在啦”,这个方法的好处是省资源,只在对可能过期的键进行操作时才花费一点CPU时间,但坏处也很明显,如果一个键永远没人再访问,那即使它早过期了,也会一直占着内存,成了“僵尸键”,这就好比家里角落的过期杂志,你不去翻它,它就在那儿一直堆着。

光靠“懒”的肯定不行,所以还有第二种策略叫 “定期删除”,Redis 会每隔一段时间(默认是每100毫秒)就随机抽取一批设置了过期时间的键出来检查,抽多少呢?不是全部,而是抽样,如果发现这批里面有过期的,就删除掉,它还有个算法控制,如果发现上一轮抽查时,过期的键比例很高,说明过期键很多嘛,那下一轮就多抽点样本,更卖力地清理,直到过期键的比例降下来,这个策略就是为了解决“僵尸键”的问题,主动进行清理。

了解了这两种机制,咱就能明白第一个常见现象了:为啥有时候数据过期了还能被读到? 很可能是因为你读取的时候,这个键刚过期但还没被“定期删除”的轮询抓到,而你的这次读取触发了“惰性删除”,所以在你读的这一刻它才被真正删除,但如果你在它被“惰性删除”前,通过其他方式(比如某些全局扫描命令)还能隐约感觉到它的存在,就会觉得奇怪,不过一般来说,对应用层面,只要是通过正常 GET 命令读,是不会读到过期数据的,因为读的时候会触发检查。

比单个键过期更让人头疼的,是 “缓存雪崩” 的潜在风险,这名字听起来就吓人,意思是大量的缓存数据在同一个时间点或者极短的时间段内集中过期失效,你有一批缓存数据,都是在凌晨两点钟设置的,过期时间都是24小时,那么到了第二天凌晨两点,这批数据会同时失效,这时候,如果突然有大量请求涌进来,发现缓存都没了,所有的请求就会直接砸向后端的数据库(比如MySQL),数据库短时间内承受巨大的压力,轻则响应变慢,重则直接挂掉,进而导致整个系统崩溃,这就好比一个超市,所有促销商品都在同一分钟恢复原价,结算台瞬间就被询问的顾客挤爆了。

那怎么避免这种慌乱的局面呢?这事儿得提前预防,一个很有效的方法是 打散过期时间,不要在代码里写死成一个固定的过期时间,比如全都 expire 3600(一小时),可以在设置过期时间时,加一个随机数,基础时间是一小时,然后再加上一个零到几分钟的随机值。expire 3600 + random(0, 300),这样,虽然数据还是在一小时左右失效,但失效的时间点就被均匀地摊开了,避免了同一时刻的集体“阵亡”,这个方法简单却非常有效,是处理潜在雪崩的第一道防线。

另一个常见的场景是 “缓存穿透”,这跟过期不太一样,但容易混淆,也提一下,它指的是用户(可能是恶意攻击者)疯狂请求一个根本不存在的数据,因为这个数据在缓存里肯定没有(不仅过期,是压根不存在),所以每次请求都会穿透缓存去查数据库,导致数据库压力巨大,对付这个问题,一个办法是 缓存空值,即使数据库查不到,也在 Redis 里存一个空值(NULL)并设置一个较短的过期时间(比如几分钟),这样,短时间内再有同样的请求过来,缓存就能拦住,不会再去折腾数据库了。

别忘了给 Redis 做好监控,看看内存使用情况,观察一下过期键的清理速度是否正常,如果发现内存持续增长,而过期键很多,可能需要调整一下 Redis 关于过期淘汰的策略配置(maxmemory-policy),让它在内存不足时能更积极地把一些数据清理掉。

Redis 的过期机制本身很聪明,是性能和资源消耗之间的一种平衡,咱们作为使用者,关键是要理解它的工作原理,知道可能会有哪些“坑”,然后针对性地做些预防措施,比如打散过期时间、缓存空值应对穿透,把这些想明白了,准备工作做足了,等到真遇到相关问题时,你就能气定神闲地排查解决,自然不会慌乱了。

Redis过期失效那些事儿,聊聊怎么处理才不慌乱