Redis分布式锁遇到的问题(面试Markdown结构化)
1. 加锁不原子,出现死锁
- 问题:分开执行
set和expire,set成功后Redis宕机,没设置过期时间,锁永久存在,其他线程永远拿不到锁。 - 解决:使用
set key value NX EX一条命令原子加锁。
2. 锁被其他客户端误删除
- 问题:业务执行时间超过锁过期时间,锁自动释放;别的线程抢到锁,原线程执行
del,直接删掉别人持有的锁。 - 解决:加锁存入客户端唯一标识;释放锁用Lua脚本,校验value属于自己才删除,禁止直接del。
3. 业务没执行完,锁提前过期释放
- 问题:业务逻辑耗时 > 锁TTL,锁过期释放,多个线程同时执行业务,并发冲突。
- 解决:看门狗续期(Redisson),业务没结束就不断延长锁过期时间;不要设置过小锁超时。
4. 主从架构下锁丢失(单机最大坑)
- 问题:master加锁成功,锁还没异步同步到slave,master宕机,slave晋升为主节点,锁消失,多个客户端获取锁。
- 解决:
- 方案1:RedLock红锁(有缺陷,很少线上使用)
- 方案2:强一致性场景改用Zookeeper/Etcd
5. RedLock自身带来的问题
- 时钟漂移、NTP时间跳变,导致锁提前失效
- 客户端GC停顿、网络延迟,锁过期,依然会锁失效
- 需要维护多套独立Redis节点,性能差、运维成本高
6. 锁不可重入
- 问题:同一个线程,递归/嵌套再次获取同一个锁,直接加锁失败,造成自己把自己阻塞。
- 解决:Redisson内部实现可重入锁,记录线程持有计数;自己手写很难实现。
7. 锁等待、死锁(线程A拿锁,线程B无限阻塞)
- 问题:获取锁失败后,如果无限循环自旋,会大量消耗CPU,压垮Redis。
- 解决:设置最大等待时间,加退避重试,不要死循环抢锁。
8. 网络抖动问题
- 问题:Redis已经拿到锁,网络超时,客户端没收到返回;客户端认为加锁失败,实际锁已经存在,资源死锁。
- 解决:设置合理的网络超时,失败主动释放所有节点残留锁。
总结
手写Redis分布式锁很难做到完备,会踩原子性、误删、锁过期、主从丢锁、不可重入、自旋风暴这些坑;开发中优先直接使用Redisson,不要自己实现。
如果业务对互斥要求极高,不建议用Redis分布式锁。