Redis里那个key到底怎么锁又怎么解,搞清楚这事儿不难但挺关键
- 问答
- 2026-01-01 11:19:25
- 3
关于Redis里怎么锁和怎么解,这事儿说白了就是防止“撞车”,想象一下,一个仓库里有个宝贝,好几个人都想同时进去拿,如果没个规矩,大家一窝蜂冲进去,肯定乱套,甚至把宝贝弄坏,Redis的锁就是这个规矩,保证同一时间只有一个人能进去操作。
这个规矩,在Redis里最常用的实现方式,就是用一个特殊的key来代表一把锁,这个key存在,就表示锁已经被别人拿走了;key不存在,就表示锁是空闲的,你可以去拿。
第一部分:怎么锁?—— 安全地拿到钥匙
锁的操作,核心命令是 SET key value NX PX 30000,我们来拆开看这个命令,这是关键中的关键(来源:Redis官方文档对SET命令参数的说明)。
key:这就是锁的名字,比如你想锁住用户123的账户,防止同时扣款,锁的key可以叫"lock:user:123",取个清晰的名字很重要。value:这是锁的值。这是个非常关键的细节,但容易被忽略。 你不能随便设个值,比如都设成“1”,因为当你解锁的时候,你需要证明“这个锁是我上的”,这个value必须是一个全局唯一的值,比如一个随机的长字符串,或者一个UUID(通用唯一识别码),这样,只有持有这个唯一凭证的人,才有资格解锁。NX:意思是“只有在这个key不存在的时候,才设置它”,这就是锁的核心机制,如果key已经存在(锁被别人占了),那么这次SET操作就会失败,返回nil,告诉你没拿到锁。PX 30000:这是给锁设置一个过期时间,比如30000毫秒(30秒)。这可能是第二重要的点。 为什么要设过期时间?想象一下,如果拿到锁的程序(比如一个Java应用)在执行业务逻辑时突然崩溃了,没来得及释放锁,那么这个锁就会永远存在,其他所有进程就再也拿不到锁了,这就是“死锁”,设置了过期时间,即使程序崩溃,锁也会在30秒后自动消失,避免了死锁的发生。
一个完整的加锁过程是这样的:你的程序在要操作共享资源前,向Redis发送 SET lock:user:123 随机唯一字符串 NX PX 30000 命令,如果Redis返回OK,恭喜你,你成功拿到了锁,可以放心进去操作了,如果返回nil,说明锁已被占用,你需要等待或者直接放弃。

第二部分:怎么解?—— 安全地归还钥匙
解锁听起来简单,不就是把那个key删掉吗?用 DEL lock:user:123 不就行了?不行,这样会出大问题。
问题在于,你不能删别人加的锁,举个例子:
- 进程A拿到了锁,设置的过期时间是30秒。
- 进程A开始处理业务,但这个业务比较复杂,花了35秒还没做完。
- 因为30秒过期时间到了,Redis自动把进程A的锁给删了。
- 这时,一直等在旁边的进程B一看锁没了,立刻成功加锁。
- 进程B刚开始操作,进程A的业务终于做完了,然后它执行了
DEL命令。 - 结果就是,进程A错误地删除了进程B刚加的锁!这下子,进程C也能进来插一脚,整个秩序就乱套了。
解锁不能简单粗暴地删除key。正确的解锁姿势是一个原子操作:先比对自己是不是锁的主人,如果是,再删除。(来源:Redis官方建议使用Lua脚本保证原子性)。

在Redis里,我们可以用Lua脚本来实现这个原子操作,因为Lua脚本在执行时是不会被其他命令打断的,脚本内容大致如下:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
这个脚本的意思是:
KEYS[1]就是锁的key,"lock:user:123"。ARGV[1]就是你当初加锁时设置的那个唯一随机值。- 脚本会先检查当前锁的值,是否和你传过来的唯一值相等。
- 如果相等,说明你就是锁的主人,然后执行删除操作。
- 如果不相等,说明你早已不是锁的主人了(可能因为超时被其他人抢占了),那就什么都不做。
通过这个方式,解锁操作就变得安全了,绝不会误删别人的锁。
第三部分:实践中还要注意啥?

搞清楚了基本的锁和解锁,还有一些现实问题要考虑。
-
锁的超时时间设多长? 这是个难题,设短了,业务没处理完锁就释放了,会导致多个进程同时进入临界区,数据可能出错,设长了,万一持有锁的进程挂了,其他进程要等很久才能继续,通常的做法是,这个时间要设置得比绝大多数业务执行时间都要长一些,并且最好有个监控机制,对于执行时间过长的任务给出告警。
-
加锁失败怎么办? 如果没拿到锁,常见的策略有:
- 直接返回错误:告诉用户“系统繁忙,请稍后再试”。
- 轮询重试:隔一段时间(比如100毫秒)再试一次,直到拿到锁或超过重试次数,但要注意,如果很多人重试,会给Redis造成压力。
- 使用阻塞队列:更高级的做法是,把请求排队,让拿到锁的进程处理完后通知下一个,但这需要更复杂的机制。
-
这还不是万无一失的:上面说的这种锁,在Redis单机模式下是没问题的,但如果Redis是集群模式,主从节点之间数据同步有延迟,可能会在极端情况下出现两个客户端同时认为自己持有锁的情况,如果你需要应对这种极端场景,可能需要更复杂的算法,比如Redlock,但那又复杂得多,对于绝大多数应用场景,我们上面讨论的单Redis实例锁已经足够可靠。
Redis锁的核心就两点:用 SET NX PX 安全地加锁(带上唯一值和过期时间),用比对value再删除的原子脚本安全地解锁,搞清楚这个流程,你就能在需要协调多个进程或服务时,有效地避免数据混乱,保证业务的正确性,这事儿确实不难,但每个细节都挺关键,马虎不得。
本文由水靖荷于2026-01-01发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/72420.html
