利用 Redis 实现分布式锁,最常用且可靠的方式是使用 SETNX + EXPIRE
或更优的原子命令 SET
命令的 NX 和 EX 选项,并结合合理设计避免常见问题。
✅ 一、基本实现(Redis 2.6.12+ 推荐写法)
SET lock_key unique_value NX EX 30
NX
:键不存在时才设置(类似 SETNX)EX 30
:设置过期时间为 30 秒(防止死锁)unique_value
:当前线程唯一标识(如 UUID),用于安全释放锁
✅ 原子操作,避免 SETNX 和 EXPIRE 分开执行的并发问题。
✅ 二、加锁(Lock)
SET resource_name unique_value NX EX 30
成功:返回
OK
,获得锁失败:返回
(nil)
,锁已被占用
✅ 三、释放锁(Unlock)——必须校验唯一值
-- 使用 Lua 脚本保证原子性 if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end
❗ 必须用 Lua 脚本:防止检查和删除之间被其他线程抢占,造成误删。
⚠️ 四、要注意的关键问题
问题 | 说明 | 解决方案 |
1. 死锁(未释放锁) | 线程崩溃或异常退出,锁未释放 | 设置 自动过期时间(EX),如 30 秒 |
2. 锁过期,业务未执行完 | 锁提前过期,多个线程同时持有锁 | 使用 看门狗(Watchdog)机制:Redisson 等客户端支持自动续期 |
3. 误删其他线程的锁 | 释放锁时未校验持有者 | 释放时比较 |
4. 主从切换导致锁失效 | 主节点未同步到从节点就宕机,从节点提升为主,锁丢失 | 使用 Redlock 算法(多个独立 Redis 实例),但性能低、争议大 |
5. 时钟漂移 | 多个 Redis 实例间时间不一致,影响 Redlock | 尽量避免使用 Redlock,或严格同步系统时间 |
✅ 五、最佳实践建议
使用成熟的客户端库:如 Redisson(Java)、Lettuce 等,内置分布式锁、看门狗、自动续期。
设置合理的过期时间:基于业务执行时间评估,避免太短或太长。
唯一值 + Lua 脚本释放锁:防止误删。
考虑高可用场景:若对一致性要求极高,建议使用 ZooKeeper 等 CP 系统。
避免长时间持有锁:锁粒度要小,尽快释放。
✅ 总结
Redis 分布式锁核心:
SET key unique_value NX EX timeout
+ Lua 脚本释放 + 过期时间 + 唯一标识
⚠️ 虽然简单高效,但要注意锁过期、误删、主从不一致等问题,不适合强一致性场景。
✅ 在大多数高并发、最终一致的场景下(如秒杀、库存扣减),Redis 锁是高效实用的选择。
0条评论
点击登录参与评论