Redis 分布式锁的原子性操作: SETNX lock value EX 10和SET lock value NX EX 10的区别
·
这是一个非常经典且容易混淆的问题,涉及到 Redis 分布式锁的原子性操作。我们来深入分析:
SETNX lock value EX 10
和
SET lock value NX EX 10
这两条命令看似相似,但第一条是错误用法,第二条才是正确的分布式锁实现方式。
一、逐条解析
❌ 1. SETNX lock value EX 10
SETNX是一个独立命令:SET if Not eXists- 它 不支持
EX参数! - 这条命令在 Redis 中是 语法错误,会返回:
(error) ERR syntax error
📌 原因:
SETNX只接受两个参数:key 和 value。你试图加上
EX 10(过期时间),但SETNX不认识这个参数,所以报错。
✅ 2. SET lock value NX EX 10
- 这是 Redis 2.6.12+ 版本引入的复合命令。
SET命令支持多个修饰符组合使用:NX:Only set the key if it does not exist.EX:Set the specified expire time, in seconds.
等价于:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
执行逻辑:
- 如果
lock不存在,则设置lock = value,并设置过期时间为 10 秒。 - 如果
lock已存在,则什么都不做,返回nil。
✅ 这是一个原子操作,完美实现“加锁 + 设置过期时间”一步到位。
二、为什么不能分开执行?
有些人可能会想:
# 步骤1:先设值
SETNX lock value
# 步骤2:再设过期时间
EXPIRE lock 10
❌ 问题出在哪里?
如果在 SETNX 成功后,EXPIRE 执行前,服务宕机了,会发生什么?
Thread A:
SETNX lock value → 成功(返回 1)
↓ 系统崩溃!
EXPIRE lock 10 → 未执行!
结果:
- 锁被成功获取,但没有设置过期时间!
- 这个锁会永远存在,导致其他线程永远无法获取锁 → 死锁!
三、正确姿势:使用 SET ... NX EX 原子操作
✅ 正确命令:
SET lock my_random_value NX EX 10
- 原子性:加锁和设置过期时间是一个操作,不会中断。
- 防死锁:即使客户端崩溃,10 秒后锁自动释放。
- 可重入性:通过
my_random_value(如 UUID)标识锁持有者,避免误删。
四、完整分布式锁流程(含解锁)
🔒 加锁
SET lock_uuid NX EX 10
🔓 解锁(必须用 Lua 脚本保证原子性)
-- 如果当前锁的 value 等于我的 uuid,才删除
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
Java 示例(使用 Jedis):
String lockKey = "lock:order";
String uuid = UUID.randomUUID().toString();
// 加锁
String result = jedis.set(lockKey, uuid, "NX", "EX", 10);
if ("OK".equals(result)) {
try {
// 执行业务逻辑
} finally {
// 解锁(Lua 脚本)
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uuid));
}
}
五、对比总结
| 命令 | 是否合法 | 原子性 | 是否推荐 | 说明 |
|---|---|---|---|---|
SETNX lock value EX 10 | ❌ 否 | ❌ | 绝对不要用 | 语法错误,Redis 不识别 |
SETNX lock value + EXPIRE lock 10 | ✅ 是 | ❌ | 不推荐 | 非原子,可能死锁 |
SET lock value NX EX 10 | ✅ 是 | ✅ | 强烈推荐 | 原子操作,安全可靠 |
六、高级替代方案
虽然 SET ... NX EX 是基础,但在生产环境建议使用更成熟的方案:
| 方案 | 优点 |
|---|---|
| Redisson | 提供 RLock,支持看门狗(自动续期)、可重入、公平锁等 |
| Redlock 算法 | 多实例部署,提高可用性(但有争议) |
| ZooKeeper | 强一致性,临时节点自动释放锁 |
✅ 总结
SETNX lock value EX 10是错误的!SET lock value NX EX 10才是正确的!
SETNX不支持EX参数,强行使用会报语法错误。- 必须使用
SET命令的复合形式,才能实现“加锁 + 过期”原子操作。 - 分布式锁的核心是:原子性、防死锁、可重入、高可用。
掌握这一点,你就能在面试中清晰解释“如何正确实现 Redis 分布式锁”,避免踩坑!
更多推荐


所有评论(0)