问题: 分布式锁:请说明基于 Redis 实现分布式锁的核心要点(如 SET NX EX 的原子性、锁的唯一标识、过期时间续期等);Redlock 算法解决了什么问题?

👇 点击展开/折叠 面试背诵模板(建议先背这个) 基于 Redis 实现分布式锁的核心要点:① 使用 SET key random_value NX EX seconds 保证原子性(N 不存在才设置,设置过期时间),避免死锁;② 锁的值必须为唯一标识(如 UUID),删除锁时需先校验值是否匹配再删除(需用 Lua 脚本保证原子性),防止误删其他客户端持有的锁;③ 过期时间需足够覆盖业务执行,业务超时时需主动续期(看门狗机制)。Redlock 算法基于多个独立 Redis 节点,解决单节点故障时锁失效问题,要求过半数节点加锁成功且耗时小于锁有效期,提供更强的安全性与容错性。
---

一、单节点 Redis 分布式锁的实现要点

1. 原子性加锁与过期时间

  • 核心命令:SET key value NX EX seconds
  • NX:键不存在时才能设置成功(原子性互斥)
  • EX:设置过期时间(单位秒),防止因客户端崩溃或网络中断导致锁永久占用(死锁)
  • 错误写法:先 SETNX 再 EXPIRE(两步非原子,若中间崩溃则锁永不过期)

2. 锁的唯一标识(防止误删)

  • 锁的值必须为每个客户端唯一的随机字符串(如 UUID + thread_id)
  • 释放锁时需先校验 value 是否与当前客户端匹配,匹配才能删除
  • 使用 Lua 脚本保证“判断 value + 删除”的原子性:
1
2
3
4
5
6
-- Redis Lua 脚本:保证 get 和 del 原子执行
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end

3. 过期时间续期(看门狗机制)

  • 业务执行时间可能超过锁的过期时间,导致锁提前释放,其他客户端获得锁,引发并发问题
  • 解决方案:客户端启动一个后台线程(看门狗),定期(如每 10 秒)检查业务是否还在执行,若未完成则为锁续期(重新设置过期时间)
  • Redisson 客户端内置此机制,默认续期 30 秒

二、Redlock 算法的核心思想与适用场景

为什么需要 Redlock?

单节点 Redis 分布式锁存在单点故障风险:若主节点宕机,锁数据未同步到从节点,此时另一个客户端可在新主节点上加锁成功,导致同一资源被多个客户端同时持有。

Redlock 算法(Redis 官方作者提出)

  • 部署多个(通常 5 个)独立的 Redis 节点(无主从复制关系)
  • 加锁流程:
    1. 客户端获取当前时间戳(开始时间)
    2. 依次向所有节点请求加锁(SET NX EX),使用相同的 key 和 value,每个节点设置超时时间(远小于锁有效期,如 5-10ms)
    3. 若客户端在过半数节点(N/2 + 1)上加锁成功,且总耗时小于锁有效期,则视为加锁成功
    4. 若失败,客户端向所有节点发送释放锁请求

解决的问题:

  • 容忍部分节点故障(过半数存活即可正常加锁)
  • 避免主从切换(异步复制)导致锁丢失的问题,提高分布式环境下的安全性

注意事项与局限性:

  • 实际使用场景有限,多数业务中单节点 + 自动续期(Redisson)已足够(如库存扣减、防止重复提交)
  • Redlock 实现复杂,且对时钟同步(NTP)敏感,引入额外故障点(如节点间时钟跳跃会导致锁失效)
  • 除非对安全性有极高要求(如金融级系统),一般不推荐自行实现 Redlock;单节点 + 合理超时 + 续期是主流方案

三、选型建议与常见陷阱

何时用分布式锁?

  • 幂等性接口防重(如订单提交)
  • 库存扣减防止超卖
  • 定时任务防止重复执行
  • 全局唯一 ID 生成(多机器下保证自增字段不冲突)

常见陷阱

  • 锁过期时间设置过短:业务还没执行完锁就到期,导致并发冲突。建议预估业务耗时,并启用续期机制。
  • 释放锁时未校验 value:导致释放了其他客户端的锁。务必用 Lua 脚本校验。
  • 未处理 Redis 节点故障:单节点模式可能因宕机导致整个分布式锁不可用。可考虑主从 + 哨兵,或使用 Redlock(但增加复杂度)。