Redis分布式锁被内存淘汰的根本原因是其key被配置为带过期时间且落入volatile-淘汰策略候选池;应通过DB物理隔离、禁用淘汰或改用allkeys-策略并配合看门狗续期来规避。

Redis分布式锁本身不会触发内存淘汰,但锁的key若被误配为可淘汰类型,或业务混用缓存与锁的key空间,就可能在内存压力下被悄悄删掉——这不是锁逻辑出错,而是配置和隔离没做对。
为什么锁key会被内存淘汰策略误伤?
根本原因在于:Redis不区分“锁”和“缓存”,只认maxmemory-policy配置和key是否带过期时间。如果你用SET key value NX EX 30设锁,这个key有TTL,就会被归入volatile-*类淘汰策略的候选池;而如果配置的是volatile-lru或volatile-ttl,一旦内存吃紧,Redis真会挑这些带过期时间的key下手——哪怕它是正在起作用的分布式锁。
常见错误现象:
- 锁突然失效,但业务日志没报错,也无显式
DEL调用 - 高并发场景下偶发重复执行,且复现时间点与Redis内存使用率飙升吻合
-
INFO memory里看到evicted_keys持续增长,但没人主动删锁
必须把锁key和缓存key物理隔离
不要图省事共用一个Redis DB或靠命名前缀区分。DB编号是硬隔离,比前缀可靠得多。
- 固定用
DB 0存锁(或专门划出DB 15),并在客户端连接时显式SELECT 15 - 缓存统一走
DB 1~DB 14,且全部配allkeys-lru或allkeys-lfu - 在
redis.conf中为锁专用DB禁用淘汰:虽然Redis不支持 per-DB 淘汰策略,但可通过maxmemory+noeviction组合实现事实隔离——给锁DB单独部署实例,或至少确保其maxmemory余量充足、永不触发淘汰
锁key不设过期时间?不行,但可以换种设法
不用EX参数不是好主意——这会导致死锁无法自愈。正确做法是:用PX设毫秒级过期,并配合客户端看门狗续期(如Redisson的lockWatchdogTimeout);同时,把maxmemory-policy从volatile-*切到allkeys-*类策略。
-
allkeys-lru仍可能淘汰锁key,但它按访问频次淘汰,而锁key通常写多读少,实际风险低于volatile-ttl - 更稳妥的是
noeviction,但需确保该实例内存绝对够用,否则写锁失败会直接抛异常,必须兜底重试逻辑 - 若必须用
volatile-*策略,则锁key务必不带EX/PX,改用Lua脚本+后台心跳续期,但这增加了复杂度和单点依赖
检查并监控真实淘汰行为
别只信配置文件,运行时得验证:
- 执行
CONFIG GET maxmemory-policy确认当前生效策略 - 定期查
INFO stats里的evicted_keys和expired_keys,两者差值大说明淘汰主力是volatile-*策略 - 用
MEMORY USAGE lock:order:123抽查锁key内存占用,排除因value过大(如存了完整对象JSON)间接加剧淘汰压力
最容易被忽略的一点:很多团队用StringRedisTemplate封装锁操作,却没意识到它的setIfAbsent默认不传ex参数时,底层仍可能被Spring Cache抽象注入过期逻辑——务必检查所有调用链路,确保锁key的生命周期完全由你控制。











