Redis 分布式锁:从 SETNX 到 Redisson 的坑

470 字
2 分钟
Redis 分布式锁:从 SETNX 到 Redisson 的坑
Redis 分布式锁:从 SETNX 到 Redisson 的坑

分布式锁看上去只是「加锁、解锁」两步,实际上一个坑接一个坑。

第一版:SETNX#

Boolean ok = redis.opsForValue().setIfAbsent("lock:order:1001", "1");
if (Boolean.TRUE.equals(ok)) {
try {
// 业务逻辑
} finally {
redis.delete("lock:order:1001");
}
}

问题:如果进程在持有锁时崩溃,锁永远不会被释放。

第二版:加上过期时间#

必须把「加锁」和「设置过期」做成一个原子操作:

Boolean ok = redis.opsForValue()
.setIfAbsent("lock:order:1001", token, Duration.ofSeconds(30));

不要先 SETNX 再 EXPIRE,两步之间崩溃就又回到了死锁。

第三版:防止误删#

假如业务执行超过 30 秒,锁已经过期并被别人拿走,此时你的 delete 会把别人的锁删掉。

做法:加锁时写入唯一标识(token),解锁时先比较再删除,并且这两步要用 Lua 脚本保证原子性:

if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end

第四版:续期#

业务耗时不确定时,固定的过期时间要么太短要么太长。解决方案是「看门狗」:持有锁期间,后台线程定时延长过期时间。

自己实现很容易出错,推荐直接使用 Redisson:

RLock lock = redissonClient.getLock("lock:order:1001");
lock.lock(); // 默认开启看门狗,自动续期
try {
// 业务逻辑
} finally {
lock.unlock();
}

还要考虑什么#

  • 主从切换:主节点宕机、锁尚未同步到从节点时,可能出现两个客户端同时持锁。对一致性要求极高的场景,不要只依赖 Redis,考虑数据库唯一约束或 ZooKeeper 等方案。
  • 业务幂等:锁只是减少并发冲突,不应该是唯一的正确性保证。关键操作务必做幂等设计。
  • 锁粒度:锁越细越好,避免把整个服务串行化。

小结#

分布式锁是工具,不是保险箱。理解它的边界,再配合幂等与兜底,才能写出真正可靠的系统。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Redis 分布式锁:从 SETNX 到 Redisson 的坑
https://www.s9mc.cn/posts/redis-distributed-lock/
作者
南柯一梦
发布于
2026-08-15
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
南柯一梦
三年 Java 后端 · 十二年 MC 服主 · AI 驱动开发
公告
欢迎来到南柯一梦!这里记录 Java、Minecraft 服务器与 AI 的折腾日常。
分类
标签
最新动态
站点统计
文章
6
分类
3
标签
17
总字数
2,494
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Vercel
博客版本
v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录
微信二维码
微信扫码添加
QQ:1649721249