Redis 分布式锁:SET NX、过期时间、续租、可重入、Redlock 与坑

"Redis 能不能做分布式锁"是经典问题,但更重要的是:

  • 你知道它哪里不可靠吗?
  • 你知道正确的工程边界吗?

你必须记住的 3 句话(面试直出):

  • Redis 锁的核心是:原子加锁(SET NX PX)+ 正确释放(校验 owner)+ 过期兜底
  • 真正难的是:业务执行时间不确定,所以要么保证超时足够大,要么做续租,但续租又要考虑宕机与误续。
  • Redis 锁解决的是"跨进程互斥",它不等于事务;涉及资金/强一致时要优先考虑数据库/一致性组件。

1. 正确的最小实现:SET NX PX + value=token

加锁目标:

  • 只有一个客户端能成功
  • 持锁者崩溃能自动释放
  • 非持锁者不能误删别人的锁

最小正确姿势:

  • 加锁:SET lockKey token NX PX 30000
  • 解锁:Lua 脚本做"先比 token 再删 key"(保证原子性)

Lua 解锁脚本的最小形态(你至少要能讲出"compare-and-del"这个原子性):

lua 复制代码
if redis.call('get', KEYS[1]) == ARGV[1] then
  return redis.call('del', KEYS[1])
else
  return 0
end

token 怎么设计更靠谱:

  • 以"请求级唯一"作为 owner(例如 uuid + 业务key),不要只用线程 id(跨进程无意义)
  • 关键是:持锁者能证明"我是我",而不是 token 多复杂

你要能说出:

  • 为什么不能 DEL lockKey 直接删?
    • 因为可能删掉别人的锁(锁过期后被别人拿到)。

2. 过期时间:不是越短越好

常见误区:

  • TTL 设置太短,业务还没做完锁就过期了 -> 并发进入临界区

合理策略:

  • TTL 要覆盖"P99 执行时间 + 抖动"
  • 必要时做续租

3. 续租(Watchdog):解决长任务,但会引入新的边界

续租的目标:

  • 持锁者还活着且还在执行,就延长 TTL

风险点:

  • 续租线程如果失控(例如卡死/网络抖动),可能造成锁释放延迟
  • 续租必须确保是"同一个 token 的持锁者"才能续(避免误续)

工程口径:

  • 续租是"降低误并发"的手段,不是强一致保证。

4. 可重入:用 owner + 计数实现

要点:

  • token 需要能表达 owner(线程/请求/实例)
  • 需要计数(重入次数)
  • 解锁次数要匹配

常见实现:

  • 用 Hash 结构记录 owner -> count

5. Redlock:能讲清"它解决了什么/没解决什么"就够了

Redlock 目标:

  • 在多 Redis 节点上获取多数派锁,提高单点故障下的安全性

争议点(面试别硬抬杠,讲边界):

  • 网络分区、时钟漂移、客户端暂停(GC/Stop-the-world)等情况下,仍可能出现复杂边界

落地建议:

  • 大多数业务场景:单 Redis + 正确 token 校验 + TTL + 续租 + 幂等重试 已足够
  • 强一致/强安全:优先 ZK/etcd 或数据库层方案

6. 常见坑(线上真会出事)

  • 坑 1:锁过期导致并发进入

    • TTL 太短或任务抖动大。
  • 坑 2:误删别人锁

    • 未校验 token,直接 DEL。
  • 坑 3:锁粒度太粗

    • 一个 key 锁住全业务,吞吐下降。
  • 坑 4:临界区不可幂等

    • 锁并不能保证"永远不重入",一定要让关键操作幂等。

更工程化的一句话:

  • 锁是"降低并发概率",幂等/状态机/唯一约束才是"把最终结果做对"。

7. 线上排查:为什么"锁明明加了还出并发"

先按这个顺序排:

  • 是否用的是 SET NX PX(是否原子)
  • 解锁是否 Lua 校验 token
  • TTL 是否覆盖 P99(是否会提前过期)
  • 是否存在 GC/线程暂停导致"实际执行时间远超预期"

常见现象:

  • 少量请求偶发双写/重复扣减
  • 日志显示两个实例在同一时间处理同一业务 key

工程修复:

  • 增大 TTL + 续租
  • 临界区幂等(唯一约束/状态机)
  • 锁粒度按业务 key 分段

8. 自测清单(你要能顺口讲出来)

  • Q:Redis 锁为什么要 token?

    • A:为了释放时校验 owner,避免锁过期后误删别人的锁。
  • Q:为什么 TTL 不是越短越好?

    • A:太短会导致业务未完成锁就过期,并发进入临界区。
  • Q:Redis 锁能保证强一致吗?

    • A:不能;它是互斥手段,仍需幂等与一致性兜底,强一致场景优先用一致性组件。

9. 30 秒背诵稿

Redis 分布式锁的正确核心是 SET NX PX 原子加锁、value 用 token 标识 owner,并用 Lua 脚本校验 token 后再删保证原子释放,同时用过期时间做崩溃兜底。难点在于业务耗时不确定,TTL 太短会导致锁过期并发进入,因此需要合理 TTL 或续租,同时临界区必须做幂等。Redlock 通过多数派提升容错,但仍有网络分区与暂停等边界,强一致场景更应优先选择 ZK/etcd 或数据库方案。

相关推荐
IT小崔2 小时前
SqlSugar 使用教程
数据库·后端
GIS阵地2 小时前
QgsProviderMetadata 详解(基于 QGIS 3.40.13 API)
数据库·qt·arcgis·oracle·gis·开源软件·qgis
qq_366086222 小时前
sql server OUTER APPLY使用
数据库·sql·mysql
Sunshine for you2 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
qwehjk20082 小时前
如何从Python初学者进阶为专家?
jvm·数据库·python
zzh0812 小时前
Mysql数据库备份与恢复笔记
数据库·笔记·mysql
枕布响丸辣3 小时前
MySQL 数据库备份与恢复全攻略:从基础到实战
数据库·oracle
IvorySQL3 小时前
PostgreSQL 技术日报 (3月31日)|五大内核模块补丁评审与问题修复汇总
数据库·postgresql·开源
IvorySQL3 小时前
最后 1 天!HOW 2026 早鸟票收官,赴济南解锁开源数据库未来
数据库·postgresql·开源