真实面经题目 · 原创解析

分布式锁的使用场景?

分布式锁用于解决多个进程、多个实例、多个节点同时争抢同一份业务资源时的互斥问题,典型场景包括库存扣减、定时任务去重、幂等控制和资源抢占。面试回答不能只说“防止并发”,还要讲清锁的粒度、过期时间、唯一 token、续期、释放校验、可重入、公平性,以及 Redis、ZooKeeper、数据库方案在性能、一致性和故障边界上的取舍。

出现于:阿里巴巴 · 后端开发

60 秒回答模板

分布式锁的核心使用场景,是在单机 synchronized 或 ReentrantLock 无法覆盖的情况下,为跨进程、跨 JVM、跨机器的共享资源访问提供互斥控制。比如电商库存扣减,同一个商品可能被多个服务实例同时处理下单请求,需要保证校验库存和扣减库存这段临界区不会被并发破坏;再比如分布式定时任务,部署了多台机器但某个清算、补偿、对账任务同一时间只能执行一次,就可以用锁做任务抢占和去重。它也常用于幂等控制,例如同一订单支付回调、退款回调、优惠券领取请求可能重复到达,需要按业务键加锁,避免重复执行副作用。实现上要根据场景选择 Redis、ZooKeeper 或数据库:Redis 性能高、延迟低,适合高并发短临界区,但必须设置过期时间、唯一 token、释放校验和必要的续期;ZooKeeper 更强调一致性和会话语义,适合对锁正确性要求更高、并发量相对可控的场景;数据库唯一索引或行锁实现简单,适合低频后台任务或已有强事务边界的业务,但吞吐和等待成本较高。使用分布式锁时还要明确它不是万能一致性方案,锁超时、进程暂停、网络分区、主从切换、业务执行过久都可能造成边界问题,所以关键业务仍要结合幂等、状态机、乐观锁、唯一约束和补偿机制兜底。

考点 核心定义
主线 库存扣减
易错点 只回答“防止并发”而没有说明跨进程、跨 JVM、跨机器…

深入解析

01

核心定义

分布式锁解决的是跨进程互斥问题,也就是多个服务实例不在同一个 JVM 内,无法共享本地内存锁时,仍然要对同一个业务资源建立排他访问。这里的资源可以是一个商品库存、一个订单、一个用户账户、一条任务记录,也可以是一台设备或一个外部接口配额。判断是否需要分布式锁的关键不是“有没有并发”,而是“并发请求是否会同时修改同一份有副作用的资源,并且普通数据库约束或原子操作不足以表达业务临界区”。

02

库存扣减

库存扣减是最常见的回答场景,但面试中要说到细节:锁的粒度通常应该落在商品、SKU 或活动库存维度,而不是全局一把大锁,否则吞吐会被严重压低。加锁保护的临界区一般包含读取可售库存、校验购买资格、扣减库存和写入关键状态,目标是避免超卖、重复扣减或资格被并发绕过。不过高并发秒杀里,分布式锁未必是最佳主方案,通常还会结合 Redis 原子扣减、数据库乐观锁、消息队列削峰和最终一致性补偿。

03

任务去重

分布式定时任务在多实例部署后容易出现重复执行,例如每天凌晨的对账、账单生成、缓存预热、数据归档和失败订单补偿。如果每台机器都触发同一任务,可能导致重复写入、重复通知或外部接口重复调用。分布式锁可以让多个实例在同一个任务键上竞争,只有抢到锁的实例执行任务,其他实例跳过或等待。这里要特别关注任务执行时长和锁过期时间,如果任务超过锁 TTL 仍未完成,另一个实例可能重新抢到锁并并发执行。

04

幂等控制

幂等场景下,分布式锁常按业务唯一键加锁,比如订单号、支付流水号、退款单号、用户活动参与键。它的目的不是单纯排队,而是在重复请求、回调重试、用户连点、消息重复投递时,保护“查询状态并执行副作用”的临界区。例如支付回调先查订单是否已支付,再更新订单、发券、发消息,如果没有互斥或幂等约束,重复回调可能造成重复发放。正确做法是锁配合状态机、唯一约束和请求流水表,不能只依赖锁。

05

资源抢占

资源抢占类场景包括抢占设备控制权、抢占任务分片、抢占导出额度、抢占优惠券领取资格或抢占某个外部系统调用窗口。分布式锁能表达“同一时间只能有一个持有者使用该资源”,但要先定义资源标识和占用周期。短时间临界区适合用自动过期锁,长时间占用则需要租约续期、心跳检测或资源状态表。否则持有者崩溃后资源可能长期不可用,或者持有者被误判失效后出现两个执行者同时操作资源。

06

实现取舍

Redis 实现通常依赖 SET key value NX PX ttl 获取锁,用 value 保存唯一 token,释放时用 Lua 校验 token 后删除,优点是性能好、实现成本低,缺点是故障切换和极端暂停下存在边界。ZooKeeper 通常用临时顺序节点实现锁,利用会话失效自动释放,并可通过顺序节点提供接近公平的排队语义,正确性更强但延迟和运维成本更高。数据库方案可用唯一索引插入锁记录、select for update 或状态字段 CAS,简单可靠但性能较低,适合低频、强事务关联的后台业务。

07

工程边界

分布式锁必须讨论过期时间、唯一 token、续期和释放校验。过期时间防止持锁进程崩溃后死锁,但 TTL 太短会导致业务没做完锁已失效,TTL 太长会拖慢故障恢复。唯一 token 用来证明当前释放者仍是锁的拥有者,避免误删别人新获得的锁。续期适合执行时间不确定的任务,但续期线程也可能因 GC、网络抖动或进程卡顿失效。可重入、公平性和锁等待策略也要按场景选择,不是所有业务都需要,盲目追求会增加复杂度。

易错点

  • 只回答“防止并发”而没有说明跨进程、跨 JVM、跨机器的互斥边界。
  • 把分布式锁当成万能一致性方案,忽略数据库约束、状态机和幂等兜底。
  • Redis 加锁后直接 delete 释放,没有唯一 token 校验,可能误删其他实例的锁。
  • 没有设置过期时间或过期时间随便拍脑袋,导致死锁或业务未完成锁已失效。
  • 库存扣减场景使用全局大锁,导致无关商品请求被串行化,吞吐急剧下降。
  • 只讲 Redis,不比较 ZooKeeper 和数据库方案的性能、一致性、运维成本差异。

面试官追问

Redis 分布式锁为什么要设置过期时间?

过期时间是为了避免持锁实例宕机、进程被杀或网络断开后锁永远不释放,造成其他实例长期无法处理资源。但 TTL 不是越短越好,太短会让业务未执行完锁就失效,引发并发执行;太长则会降低故障恢复速度。

为什么释放锁时要校验唯一 token?

因为锁可能已经过期并被其他实例重新获取,原持有者如果继续执行 delete,就可能把别人的锁删除。唯一 token 表示锁的拥有者身份,释放时先比较 token 是否一致,再删除锁,才能避免误释放。

Redis、ZooKeeper、数据库实现分布式锁怎么选?

高并发、短临界区、对延迟敏感的场景通常选 Redis;对一致性、会话失效、排队顺序要求更高的协调场景更适合 ZooKeeper;低频后台任务或需要和数据库事务强绑定的场景,可以用数据库唯一索引、行锁或状态 CAS。

分布式锁能解决库存超卖吗?

可以作为防超卖的一种手段,但不应是唯一手段。库存扣减还需要数据库层面的条件更新、乐观锁、唯一订单约束、支付状态校验和异常补偿。高并发秒杀场景更常用原子扣减和队列削峰,锁只保护必要的临界区。

分布式锁需要支持可重入和公平性吗?

不一定。可重入适合同一线程或同一业务链路中重复进入同一资源临界区的场景,但会增加持有计数和身份识别复杂度。公平性适合排队要求明确的资源分配,但会牺牲吞吐,普通短任务锁通常不强求公平。