Redis锁怎么弄得更靠谱点,别让它过期失效那种感觉
- 问答
- 2026-01-17 06:36:53
- 4
想象一下,你设置了一个10秒过期的锁,万一你的业务逻辑特别复杂,10秒内没搞定,锁自己没了,另一个线程趁机加锁成功,这时你的程序还在欢快地修改数据,结果就是数据彻底乱套,这就是最让人头疼的“锁失效”问题。
靠谱的锁需要一个“守护线程”,也就是我们常说的 “看门狗”(Watchdog)机制,这个思路在很多分布式锁的实现里都有,比如Redisson这个Java客户端(来源:Redisson官方文档)。
“看门狗”是怎么工作的?
- 加锁时留后路:你去加锁,比如希望这个锁能持有30秒,但你在设置Redis锁时,不要直接设30秒的过期时间,而是设一个比较短的时间,比如10秒(这个时间要保证远大于网络请求Redis的时间),关键在于,同时启动一个后台线程(看门狗),这个线程的任务就是定时去给这个锁“续命”。
- 定时续命:看门狗线程会每隔一段时间(过期时间的三分之一,10秒过期就每3秒一次)去检查一下你这个客户端是否还持有这个锁,如果还持有,它就执行一个
EXPIRE命令,把锁的过期时间重新设置为10秒,这样一来,只要你的主业务线程还在正常运行,这个锁就永远不会因为过期而自动释放。 - 释放时清场:当你的业务逻辑执行完毕,主动释放锁的时候,除了删除那个锁的key,还要记得关掉那个对应的看门狗线程,别让它再傻乎乎地续命了。
这样一来,锁的存活时间就和你的业务逻辑执行时间绑定了,而不是和一个冰冷的数字绑定了,只有你的程序挂了(比如服务器宕机),看门狗线程也跟着挂了,没人续命了,锁才会在短暂的10秒后过期释放,其他客户端才能获取到锁,这保证了锁的释放一定是安全的。
光有“续命”还不够,加锁和解锁本身也得是原子的。
你可能会用SET key value NX PX 30000这个命令来加锁,这已经是原子操作了,没问题,但解锁的坑更大,最经典的错误就是:
- 客户端A获取了锁。
- A执行任务,花了点时间,锁差点过期。
- A任务完成,准备解锁(删除key)。
- 就在A发出删除命令前,锁恰好过期了。
- 客户端B趁虚而入,成功获取了锁。
- 这时,A的删除命令到了,直接把B刚创建的锁给删了!
为了解决这个问题,解锁时必须要验证锁的价值(value),具体做法是:

- 加锁时,value不要用简单的“1”或“true”,而要用一个全局唯一的值,比如UUID或者雪花算法生成的ID。
- 解锁时,先GET一下锁的value,看看是不是自己当初设置的那个唯一ID,如果是,才能执行删除操作。
但GET和DEL是两个命令,不是原子的,中间还可能被插队,我们需要用Lua脚本来保证解锁操作的原子性(来源:Redis官方推荐使用Lua脚本处理复杂原子操作),Lua脚本在Redis里是单线程执行的,能确保一连串命令不被中断。
一个简单的解锁Lua脚本长这样:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
这段脚本的意思就是:如果锁(KEYS[1])的值等于我传过来的值(ARGV[1]),那我就删除它;否则,说明锁已经不属于我了,我什么都不做。
还有,你得考虑Redis本身会不会挂掉。

如果你的业务非常关键,丢锁的代价极高,那么单节点的Redis锁依然有风险(主从切换时可能丢数据导致锁失效),这时你可能需要用到RedLock算法(来源:Redis作者Antirez的博客)。
RedLock的核心思想是分散风险,它要求你同时向N个(通常是5个)独立的Redis主节点(不是主从关系)申请锁,只要超过半数的节点(比如5个里拿到3个)都加锁成功,并且总的耗时小于锁的过期时间,才算加锁成功,释放锁时,也要向所有节点发起释放请求。
这样做的好处是,即使其中一两个节点宕机了,只要大多数节点还活着,锁的状态就是安全的,这大大提高了可靠性,但代价是性能下降,架构变复杂,除非你真的需要那么高的可靠性,否则用带看门狗的单节点Redis锁已经能解决99%的问题了。
弄个靠谱的Redis锁,你需要:
- 核心机制:实现一个看门狗(Watchdog)线程,给锁自动续期,避免业务未完成锁先过期。
- 安全解锁:加锁时设置唯一value,解锁时通过Lua脚本原子性地验证value并删除。
- 高阶保障:对可靠性要求极高的场景,考虑使用RedLock算法,在多个Redis实例上分散风险。
把这些点都做到,你的Redis锁就从“勉强能用”升级到“相当靠谱”了。
本文由度秀梅于2026-01-17发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/82254.html
