首页/数据/数据库速查/分布式锁:SET NX PX 原子加锁
Redis 运维与场景124 条

分布式锁:SET NX PX 原子加锁

Redis 分布式锁的标准加锁方式:用一条 SET key value NX PX ms 同时完成「不存在才创建」与「过期自动释放」,value 用唯一 token 标识持有者,便于安全释放。

数据库速查数据更新于 2026-10-13

分布式锁怎么加

分布式锁的标准加锁方式是单条命令 SET 键 唯一值 NX PX 毫秒:一次操作同时完成「不存在才创建」与「过期自动释放」两件事,避免了先判断再设置之间的竞态。值用唯一标识,比如请求编号或机器加线程加时间戳,释放时先校验这个标识再删除,防止误删别人的锁。

基本信息

语法SET lock:资源 唯一标识 NX PX 毫秒
唯一标识请求编号或机器线程时间戳组合,释放时校验
成败判断返回 OK 表示加锁成功,返回空值表示未拿到
过期时间建议取业务耗时的数倍,长任务需要续期
释放方式先校验标识再删除,不能直接删除键
适用范围单实例可靠,集群部署要按对应方案实现

典型使用场景

  • 防止同一订单被重复处理:处理前加锁,完成后释放
  • 定时任务多副本互斥:只允许一个副本执行
  • 库存扣减等关键区间的并发控制
  • 缓存重建防击穿:只放一个请求去回源
  • 消息重复消费的幂等控制
  • 资源池分配:同一资源同一时刻只分配给一个消费者

加锁与观察

加锁与观察
# 加锁:NX 表示不存在才设置,PX 为毫秒级过期
SET lock:order:42 6f1d2c4e NX PX 10000
# 返回 OK   表示拿到锁
# 返回空值 表示别人持有,稍后重试或快速失败
GET lock:order:42   # 查看锁当前的值,调试用
PTTL lock:order:42   # 查看剩余过期时间

安全释放思路

安全释放思路
# 释放必须校验持有者标识:只有值等于自己的标识才删除
# 逻辑示意(真实实现用脚本或客户端封装保证比较与删除的原子性)
IF GET lock:order:42 == 6f1d2c4e THEN DEL lock:order:42
# 值不等于自己的标识说明锁已过期并被别人获取,直接删除会误删他人的锁

加锁结果读法

返回含义处理
OK成功创建锁,当前请求持有进入临界区,处理完主动释放
空值键已存在,别人持有锁重试或快速失败,不要长时间阻塞
剩余过期时间为正持有者尚未超时正常状态
键不存在锁已释放或已经过期可以重新尝试加锁
值不是自己的标识锁已被他人获取不要删除,避免误删
过期时间过短临界区未完成锁就失效需要续期机制或加大过期时间
释放锁不能直接删除键:必须校验值是否等于自己的标识,否则任务超时后锁已易主,直接删除会把别人的锁删掉,互斥彻底失效。
唯一标识既用于释放校验也用于排查:把请求编号或线程信息写进值里,出现并发问题时能直接看出是谁持有了锁。
过期时间是锁的安全网:太短会让临界区没执行完就失锁,太长会让故障后长时间无法重新加锁,长任务应在处理过程中按需续期。

加锁使用清单

  • 加锁用单条条件写入命令,不要拆成判断加设置两步
  • 值必须是唯一标识,释放前必须校验
  • 过期时间取业务耗时的数倍并留出重试余量
  • 临界区内不要做长时间阻塞操作
  • 加锁失败要有明确的退避或失败策略
  • 多实例部署时确认锁实现与实例拓扑匹配
单实例方案在主节点故障切换时存在窗口:锁可能已经丢失而业务仍在执行,对一致性要求高的场景要结合业务校验或采用集群共识方案。

易错点

  • 用先判断再设置两条命令加锁:两步之间出现竞态,两个请求可能同时认为自己拿到了锁
  • 用固定字符串作为锁的值:任何客户端都能删掉别人的锁,互斥保护形同虚设
  • 释放时直接删除键:任务超时后锁已易主,会误删他人持有的锁
  • 过期时间设置过短:临界区没执行完锁就失效,并发写入依然发生
  • 加锁失败后无退避地无限重试:大量请求空转打满实例的 CPU 与连接,问题从业务扩散到存储层

数据来源与参考

  • Redis 官方文档: SET 命令
  • Redis 官方文档: 分布式锁

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌