分布式锁如何工作?

考查重点:共享互斥资格与租期失效。多进程向共同协调器原子抢资源资格,持有者执行后释放;租期失效旧任务可能仍活,需业务幂等/隔离

本题目录

参考答案

核心回答

分布式锁是让多个进程在操作同一份共享资源前,先到一个共同的协调系统中竞争访问资格。协调系统原子地决定谁获得锁,获得者执行临界区,其他请求等待、重试或失败,最后由持有者释放。 Redis、ZooKeeper、数据库都可以承载这种协调,但故障与失效语义不同。

例如,同一个缓存条目过期后,多个服务实例可能同时回源重建。本地锁只能约束本进程中的线程;让这些实例竞争同一个业务 key 对应的分布式锁,才有机会把重建合并为一次。锁只记录协调状态,真正读取数据库和写入缓存的仍然是业务进程。

它能提供的互斥还受租约、会话和故障模型约束。拿到过锁,不代表客户端可以无限期执行;锁过期或会话失效后,旧进程仍可能继续运行,需要业务资源一起处理这类风险。

正常流程怎样工作

  1. 按共享资源确定锁名。 同一资源的访问方使用同一个锁名,不相关资源尽量分开,避免一把全局锁把所有请求串行化。绕过加锁协议的写入,不会自动受到保护。
  2. 原子获取并标识持有者。 不能先查询“没有锁”,再单独写“我持有锁”,否则多个进程可能同时成功。还要区分本次获取与其他进程、其他轮次的获取,方便安全释放和续约。
  3. 在有效持锁期间执行完整临界区。 拿到锁后重新检查业务状态,避免使用等待前的旧数据;需要保护数据库事务时,通常应覆盖到事务真正提交,而不只是锁住查询或发起异步任务的瞬间。
  4. 只释放自己仍然持有的锁。 校验身份与释放必须在协调系统中原子完成,否则校验后锁过期、被别人重新获取,本次删除就可能误删新锁。失败、异常退出与正常路径都要考虑释放。

获取失败时,应明确立即失败、有限等待还是退避重试。加锁请求超时则意味着结果可能未知,不能一边认定失败,一边继续执行受保护的业务。

常见实现有什么区别

Redis 通常用原子条件写入保存锁名、唯一持有标识和 TTL,避免客户端崩溃后永久占锁。释放时原子地比较标识再删除,续约也只应更新当前仍属于自己的锁。详细命令和 Redis 版本行为可继续看Redis 分布式锁。

ZooKeeper 的常见方案是在同一个父节点下创建临时顺序节点,序号最小的客户端获得资格;其余客户端监听紧邻自己的前驱节点,收到删除通知后重新检查。前驱已经消失时要重新读取,不能错过通知后一直等待。监听前驱可以减少锁释放时所有等待者一起抢占的“羊群效应”。

ZooKeeper 的临时节点绑定的是会话:显式释放时删除自己创建的节点,会话过期后由集群清理。短暂断网不等于节点立刻消失,而集群确认会话过期时,失联客户端也可能还不知道这一结果。

数据库 也能通过事务行锁、唯一约束或条件更新实现部分协调需求。如果资源和约束本来就在同一数据库中,优先判断这些能力是否已足够。例如防止库存扣成负数,可以用受条件约束的原子更新;没必要仅仅因为部署了多个实例,就额外引入一套锁服务。

租约过期后,旧持有者怎么办

租约或会话机制解决的是“持有者失联后,其他人最终还能继续”的问题,但不会强制停止旧进程。典型窗口是:A 获锁后长时间暂停,锁已失效,B 获得新锁开始执行;A 恢复后仍按原计划写业务资源。

续约能减少正常长任务提前失锁的概率,但续约线程也可能被暂停或断网。客户端发现失租后应停止后续受保护操作,已经发出的远程请求却未必能撤回。因此,唯一随机标识加原子释放,只能避免 A 误删 B 的锁,不能单独拦住 A 的迟到业务写。

正确性要求更高时,可以采用 fencing token(隔离令牌):每轮有效授予产生递增代次,业务写入携带代次,资源端原子地检查并记录已接受的代次,拒绝更旧的写。例如 B 已用代次 8 更新资源后,A 携带代次 7 的请求再到达,就会被资源拒绝。

这个代次要由满足要求的协调机制产生,故障后不能随意倒退;资源端也必须真正参与校验。当前代次可能有多次合法写入,重复请求是否允许还要由业务幂等规则决定,不能仅靠“每次必须比上次更大”笼统处理。

常见追问

Redis 主从切换后,锁还可靠吗?

异步复制存在窗口:锁写入主节点成功,但尚未复制就发生切换,新主节点上可能没有这把锁,另一客户端便可能获得它。增加副本或自动续约不能直接消除这个窗口。应根据互斥失效的业务代价选择协调方案,并用资源端约束、版本校验或幂等处理补足所需保证。

怎样避免死锁和一直等不到锁?

正常路径和异常路径都释放锁,客户端崩溃由租约或会话失效处理;获取设置合理的等待上限。若同时获取多把锁,还应统一顺序、减少嵌套,避免循环等待。TTL 只能帮助锁最终失效,不会自动修正业务中的所有死锁与长时间阻塞。

分布式锁能保证订单写入和消息发送一起成功吗?

不能。锁主要协调并发进入,事务原子性和故障恢复仍需单独设计。数据库事务、唯一约束、幂等、状态机,以及可靠事件记录等各自解决不同问题,不能用“一把锁”代替。

参考资料

  • 国内:JavaGuide:分布式锁入门:多实例访问外部共享数据、锁粒度和临界区部分,解释本地锁的边界,以及为什么数据库约束有时已经足够。
  • 国内:JavaGuide:分布式锁常见实现方案:参考 Redis 原子获取释放、ZooKeeper 临时顺序节点和 fencing,比较正文中协调锁资格与资源端拒绝旧写入的不同职责。
  • 国内:advanced-java:Redis 与 ZooKeeper 分布式锁:唯一持有标识、超期误释放和异步复制切换的例子,说明锁不仅要考虑正常获取,还要考虑失效后的行为。
  • 国外:Redis:Distributed Locks with Redis:单实例获取释放、租约有效期、故障切换与 fencing 讨论,解释安全释放为什么不能代替迟到写防护。
  • 国外:Apache ZooKeeper:Recipes — Locks:创建临时顺序节点、监听前驱、重新检查与创建结果未知的处理过程,说明协调客户端怎样获得和释放资格。

相关面经

8 篇

虎牙

全栈开发
秋招二面
查看原面经 →
分布式锁和异步线程池如何设计的?

小红书

国际化开发工程师
校招秋招一面
查看原面经 →
● 业务系统的分布式锁如何设计的?作用是什么?

小鹏

后端开发工程师(AI / Agent 方向)
二面
查看原面经 →
分布式锁实现原理是怎么样的?

字节跳动

AI 应用开发工程师
秋招一面
查看原面经 →
项目里的分布式锁基于什么实现?

字节跳动

Agent 开发工程师(评测方向)
一面
查看原面经 →
分布式锁
展开剩余 3 篇面经

腾讯

全栈开发工程师
社招一面
查看原面经 →
分布式锁有了解吗?

有赞

后端开发工程师(AI / Agent 方向)
查看原面经 →
介绍分布式锁

转转

后端开发
实习一面
查看原面经 →
g. 了解过分布式锁吗?有哪些框架实现?

收录题集

1 个来源,共1 份题集

数据库高频知识点

Rocky Ding · AIGC-Interview-Book · 1 条原题

查看题集 →

相关题目

4 题