好的,我来给你整理一份Redis 实现分布式锁的 7 种方案,详细分析原理、优缺点以及适用场景,便于开发者选择。
Redis 实现分布式锁的 7 种方案
方案 1:SETNX + 过期时间
原理
- 使用 Redis 的
SETNX
(SET if Not eXists)命令保证原子性,设置 key 仅在不存在时成功 - 搭配
EXPIRE
设置过期时间,防止死锁
String lockKey = "lock";
String lockValue = UUID.randomUUID().toString();
Long acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);
if (acquired != null && acquired) {
// 获取锁成功
}
优点
- 简单易实现
- 原子性通过
SETNX
保证
缺点
- 释放锁需小心,避免误删别人锁(需校验 value)
- 如果业务时间大于过期时间,锁会自动释放导致安全问题
方案 2:SET NX PX
原理
- Redis 2.6.12+ 支持原子操作
SET key value NX PX ttl
,一次命令即可设置锁和过期时间
SET lockKey lockValue NX PX 10000
优点
- 原子性操作,一步完成锁和过期时间
- 安全性比方案 1 高
缺点
- 依然有业务执行时间超过锁时间的风险
方案 3:带过期时间的 value + Lua 脚本释放锁
原理
- value 保存唯一标识(UUID)
- 使用 Lua 脚本判断 value 是否是自己,再删除锁,保证释放原子性
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
优点
- 解决误删别人的锁问题
- 原子性释放
缺点
- 仍可能出现锁超时问题
方案 4:Redisson 实现分布式锁
原理
- Redisson 是 Java Redis 客户端,封装了分布式锁实现
- 支持可重入锁、公平锁、读写锁等
RLock lock = redisson.getLock("lock");
lock.lock(10, TimeUnit.SECONDS);
// 业务执行
lock.unlock();
优点
- 简单易用,功能完善
- 自动续期机制防止锁过期
缺点
- 依赖第三方库,增加项目依赖
方案 5:RedLock 算法(多节点 Redis)
原理
- 由 Redis 作者提出
- 在 N 个 Redis 实例上分别尝试获取锁,必须在大多数节点获取成功才算成功
- 防止单点故障,提高可靠性
优点
- 高可用,防止单节点 Redis 宕机导致锁失效
- 避免 split-brain 问题
缺点
- 实现复杂
- 对网络延迟敏感
方案 6:使用 Redis 发布/订阅 + 队列(阻塞锁)
原理
- 当锁被占用时,将请求放入等待队列
- 释放锁时,发布消息通知下一个客户端获取锁
优点
- 可以实现阻塞等待
- 避免大量客户端空循环获取锁
缺点
- 需要额外维护队列和 pub/sub 消息
- 延迟较高
方案 7:利用 Redis 的 SETBIT/INCR 实现分布式锁
原理
- 将锁状态保存在位图或计数器
- 原子性操作如
SETBIT
或INCR
判断是否已被占用 - 常用于高性能场景
优点
- 高性能,占用内存小
- 适合高并发场景
缺点
- 实现复杂,容易出错
- 可读性差,不直观
总结与建议
方案 | 安全性 | 易用性 | 适用场景 |
---|---|---|---|
SETNX + EXPIRE | 中 | 高 | 简单锁需求 |
SET NX PX | 高 | 高 | 推荐小型项目 |
Lua 脚本释放锁 | 高 | 中 | 需要保证释放安全 |
Redisson | 高 | 高 | Java 项目推荐 |
RedLock | 高 | 中 | 高可用分布式环境 |
队列 + Pub/Sub | 中 | 低 | 阻塞等待场景 |
SETBIT/INCR | 中 | 低 | 超高并发、高性能场景 |
✅ 推荐实践:
- 单机 Redis → 使用
SET NX PX
+ Lua 脚本 - 多节点 Redis → 使用 Redisson + RedLock
发表回复