Redis分布式锁需满足原子加锁、归属校验解锁、自动过期三要素:加锁用SETNX+EXPIRE或SET NX EX确保唯一性和防死锁;解锁必须通过Lua脚本校验value再删除;value须为UUID等唯一标识,超时时间应略大于业务最大耗时。

在单机多线程竞争场景下,用 Redis 的 SETNX 配合唯一请求标识(如 UUID)可以快速构建一个简易但可用的分布式锁。核心在于:**加锁必须原子、解锁必须校验归属、锁必须有自动兜底过期**。下面分三步讲清楚怎么落地。
加锁:用 SETNX + EXPIRE 保证基础可用性
虽然 SETNX 和 EXPIRE 分开调用不是原子操作,但在单机竞争且对极端可靠性要求不高的场景下,可先用它快速验证逻辑。关键点是:
- 锁 key 要业务相关、全局唯一,比如
"order:pay:10001"; - value 必须是当前线程/请求的唯一标识,推荐用
UUID.randomUUID().toString(); - 加锁成功后立即设置过期时间(如 30 秒),避免进程崩溃导致死锁;
- 若 SETNX 返回 1,说明抢锁成功;返回 0,说明锁已被占,需重试或失败退出。
解锁:必须校验 value 再删除,防止误删
直接 DEL 键是危险的——A 线程锁超时释放,B 线程拿到锁并执行,A 执行完仍调 DEL,就把 B 的锁删了。正确做法:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 用 Lua 脚本一次性完成“读 value → 判断是否匹配 → 删除”三个动作;
- 脚本内容简洁:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end; - Java 中通过
StringRedisTemplate.execute()执行该脚本,传入 key 和自己的 UUID; - 返回 1 表示删除成功,0 表示锁不属于当前线程,不应继续操作。
增强健壮性:加锁时直接用 SET 命令的 NX+EX 参数
Redis 2.6.12+ 支持 SET key value NX EX seconds,一条命令同时完成“不存在才设值 + 设置过期”,彻底规避 SETNX 后崩溃导致无过期的问题。推荐写法:
立即学习“Java免费学习笔记(深入)”;
-
redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS); - 这个方法底层就是封装了 SET ... NX EX,返回 true 表示加锁成功;
- 注意:value 仍是唯一标识,后续解锁才能做归属判断;
- 如果业务允许短超时(比如 5~10 秒),还能降低锁残留风险。
不复杂但容易忽略:锁的 value 一定要可区分,不能写死成 "1" 或 "true";超时时间要略大于最长业务耗时,但不宜过长,否则故障恢复慢。

















