高并发环境里Redis过期键怎么多线程处理才不乱真是个难题
- 问答
- 2025-12-28 16:12:55
- 3
在高并发的环境下,Redis的过期键处理确实是一个容易让人头疼的难题,核心问题就在于“时机”和“竞争”这两个词上,想象一下,Redis就像一个繁忙的共享仓库,里面堆满了各种有保质期(过期时间)的货物(键值对),有多组工人(多个客户端线程)同时在这个仓库里工作,有的在存入新货,有的在取货,而仓库本身也有一个自动清理系统(Redis的过期策略)来处理过期货物,混乱就发生在这几方动作的交织点上。
我们需要理解Redis处理过期的两种主要方式,这能帮我们看清问题的根源,根据Redis官方文档(Antirez,《Redis持久化与内存管理》相关章节)的说明,它采用的是惰性删除和定期删除相结合的策略,惰性删除很简单:当一个客户端线程来访问某个键的时候,Redis才会顺便检查一下这个键是否过期了,如果过期就立刻删除,然后返回空值,这很节能,但有个大问题:如果一个键永远没人来访问,那么即使它早过期了,也会一直占着内存,成了“垃圾”,为了解决这个问题,Redis又有了定期删除:它会每隔一段时间,随机抽取一部分设置了过期时间的键,检查并删除其中已经过期的,这个行为是Redis后台线程(在某些版本中是主线程周期性任务)自己完成的。
我们把多线程并发的场景加进来,难题就浮现了:

检查与删除之间的“时间缝隙”
这是最经典的并发问题,假设线程A和线程B同时来访问同一个键“user:123”,这个键可能刚刚在毫秒级之前过期了,但还没来得及被惰性删除或定期删除触发。
- 线程A发起读取命令,Redis执行惰性删除检查,发现“user:123”已过期,Redis决定删除它。
- 就在Redis即将执行删除操作但还没执行的那一刹那,线程B的读取请求也到了,由于删除动作还没发生,键在技术上依然“存在”。
- 这时,会发生什么?Redis是允许多个命令并发执行的(单线程事件循环处理命令,但网络IO等可能是多线程),如果处理不好,可能会出现两种情况:
- 情况一(坏情况): 线程B也通过了过期检查,因为删除还没发生,Redis先后处理A和B的删除逻辑,这可能导致重复删除(虽然结果一样,但浪费资源),更糟的是,如果这个键恰好又被重建,可能会出现极短时间的数据错乱。
- 情况二(Redis的解决): Redis的主命令处理线程是单线程的(核心逻辑),这意味着命令是排队执行的,线程A的“GET”命令会先完成整个流程(包括检查过期和删除),然后线程B的“GET”命令才会被执行,此时它发现键已经被A删除了,于是返回空值。这解决了单个Redis实例内的竞争问题,但问题会转移到应用层。
应用层逻辑的“判断陷阱”

即使Redis内部单线程保证了删除动作的原子性,应用层的多线程逻辑依然会掉坑里,一个常见的业务场景是:检查某个键是否存在,如果不存在,就去数据库加载数据并写入Redis。
- 线程A检查键“user:123”,发现不存在(可能刚过期被删)。
- 线程A于是去数据库查询用户123的数据。
- 在线程A查询数据库的这个过程中,线程B也来检查键“user:123”,同样发现不存在。
- 线程B也去数据库查询用户123的数据。
- 线程A和线程B都查询到了数据,然后先后向Redis写入键“user:123”,结果是,数据库被重复查询了两次(缓存穿透),并且两个线程可能会以微小时差覆盖对方写入的值,虽然最终数据一致,但造成了不必要的负载。
这已经不是Redis本身的问题,而是应用逻辑在并发下的一致性问题,解决这个问题需要用到额外的同步机制,比如互斥锁(分布式锁),在查询数据库前,线程先尝试获取一个针对“user:123”的锁,拿到锁的线程才有资格去加载数据,其他线程则等待或直接读取旧值(如果允许的话)。
内存压力与淘汰策略的连锁反应

当大量键几乎在同一时刻过期时,会形成一个“过期潮”,如果这些过期键没有被及时地通过惰性删除访问到,那么压力就给到了定期删除机制,在高并发写入下,内存可能迅速吃紧,Redis会触发内存淘汰机制(如LRU、LFU等)。 这个过程的并发挑战在于,内存淘汰是在内存不足时被动触发的,它可能会和客户端的读写命令、定期删除任务争抢CPU和I/O资源,虽然Redis尽力优化,但在极端情况下,这可能导致某些客户端请求的延迟飙升,因为主线程需要花费更多时间在清理内存上,而不是处理命令,这感觉上就像是“乱”了,因为响应时间变得不稳定。
集群环境下的复杂性
在Redis集群模式下,问题又升级了,一个键可能存储在节点A上,但访问它的客户端可能连接的是节点B(需要重定向),过期的传播和同步在集群中需要额外的消息传递,虽然Redis集群协议保证了最终一致性,但在某个极短的时间窗口内,不同节点对同一个键是否过期的认知可能存在细微差异,这对于强一致性要求的应用来说,又是一个需要面对的“乱象”。
高并发下Redis过期键处理的“乱”,主要体现在三个方面:一是Redis内部机制在极限并发下可能暴露的微小时序问题(主要由单线程模型缓解了);二是更主要的,应用层逻辑在“检查-加载-写入”这个流程中缺乏并发控制导致的重复工作和数据潜在风险;三是大量键集中过期与内存淘汰机制叠加时,对系统稳定性和响应延迟的冲击,解决这些难题,不能只依赖Redis本身,更需要开发者在应用层设计时,就考虑到并发场景,合理使用分布式锁、设置不同的过期时间以避免集中过期、以及监控内存和过期键的数量,做到防患于未然。
本文由畅苗于2025-12-28发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/70122.html
