Skip to content

Redis 分布式锁

Redis 分布式锁常用于控制分布式环境下的互斥访问。理解它时不能停留在“会用 SETNX”,还要关注锁的安全性、释放正确性、过期续期、Redisson 和适用边界。


1. 为什么需要分布式锁

单机锁只能控制单个 JVM 内部线程互斥,例如 synchronizedReentrantLock

分布式场景下,请求可能落到多个服务实例:

text
服务 A 实例 1
服务 A 实例 2
服务 A 实例 3

这时本地锁无法互斥,需要一个外部共享组件保存锁状态。


2. 正确的基础加锁方式

推荐使用一条 Redis 命令同时完成“加锁 + 设置过期时间”:

text
SET lock:order:1 unique-value NX EX 30

含义:

  • NX:key 不存在时才设置,保证互斥。
  • EX 30:设置 30 秒过期时间,避免客户端宕机后死锁。
  • unique-value:唯一值,用于安全释放锁。

不要用下面这种非原子写法:

text
SETNX lock value
EXPIRE lock 30

如果 SETNX 成功后服务宕机,EXPIRE 没执行,就可能死锁。


3. 为什么 value 要唯一

释放锁时必须确认:

text
这把锁是我加的,才能由我释放

否则可能出现:

  1. 线程 A 获取锁,业务执行超时,锁自动过期。
  2. 线程 B 获取同一把锁。
  3. 线程 A 执行结束,直接 DEL lock
  4. 线程 A 删除了线程 B 的锁。

所以 value 应该是唯一标识,例如:

text
clientId + threadId + randomUUID

4. Lua 脚本释放锁

释放锁需要“判断 value + 删除 key”原子执行。

Lua 脚本:

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

不能先 GETDEL,因为两条命令之间锁可能过期并被别人获取。


5. 锁过期问题

锁必须设置过期时间,但过期时间也会带来问题:

text
业务执行时间 > 锁过期时间

结果是业务还没执行完,锁已经被其他线程获取。

解决思路:

  • 合理估算锁 TTL。
  • 业务尽量短小。
  • 使用自动续期机制。
  • 对关键资源增加幂等和状态校验。

6. Redisson 看门狗机制

Redisson 的可重入锁默认有看门狗机制。

大致逻辑:

text
获取锁成功
  -> 设置默认过期时间
  -> 后台定时续期
  -> 业务执行完成后主动释放

只要持锁客户端还活着,看门狗会持续续期,避免业务未执行完锁就过期。

但要注意:

  • 如果服务发生长时间 STW,续期可能不及时。
  • 如果网络分区,Redis 侧可能认为锁已过期。
  • 分布式锁不能替代业务幂等。

7. RedLock 简介和争议

RedLock 是 Redis 官方文档中提出的多 Redis 节点分布式锁算法。

大致思路:

text
向多个独立 Redis 节点尝试加锁
超过半数成功,并且总耗时小于锁有效期,则认为加锁成功

争议点:

  • 它比单实例锁复杂。
  • 对时钟、网络延迟、故障模型有要求。
  • 如果业务需要强一致互斥,通常更适合使用数据库约束、ZooKeeper、etcd 等一致性组件。

理解它的边界时要谨慎:Redis 锁适合效率型互斥,不适合承载强一致正确性的唯一防线。


8. 分布式锁适用边界

适合:

  • 防止重复提交。
  • 控制定时任务多实例并发。
  • 热点资源短时间互斥。
  • 缓存重建互斥。

不适合单独承担:

  • 金融级强一致扣款。
  • 复杂长事务。
  • 无幂等保护的数据修改。

关键业务要配合:

  • 数据库唯一索引。
  • 状态机。
  • 幂等表。
  • 乐观锁版本号。

9. 理解检查

为什么不能只用 SETNX

因为 SETNXEXPIRE 分开执行不是原子的,可能导致死锁。应该用 SET key value NX EX seconds

为什么释放锁要用 Lua?

因为判断 value 和删除 key 必须原子执行,避免删除别人的锁。

Redis 分布式锁安全吗?

它能解决很多效率型互斥问题,但在网络分区、主从切换、长时间 GC 等场景下仍有风险。强一致场景不能只依赖 Redis 锁。


10. 总结

Redis 分布式锁的最低正确姿势是:SET NX EX 原子加锁、唯一 value 标识持有者、Lua 原子释放、合理 TTL 和续期机制。真正重要的业务还要有幂等和数据库层兜底。

Released under the MIT License.