分布式锁面试100题精讲:从Redis原理到工程落地
前几天帮一个学弟做模拟面试他刚跟着豆包把那套分布式锁面试题100道刷完觉得自己稳了结果我在电话里追问了一句——你说说你的锁在Redis主从切换的时候丢了怎么办——他当场沉默了。这个场景我在真实面试里见过太多次候选人能背出SET EX NX能说出Lua脚本释放锁但一旦问到工程落地、高可用边界、场景取舍就暴露出只是背了答案没有真正理解分布式锁。这篇文章就是想把分布式锁面试题按真正的知识地图重新过一遍。标题虽然写着100道但我不会真的干巴巴把100个问题堆给你。我的做法是按面试官的命题逻辑把分布式锁拆成五大模块每个模块配一道高频必背题的精讲、原理剖析、追问应对再附上20道子题目清单加起来正好100道。适合正在准备后端岗位面试、系统设计面试或者想把手头Redis锁用得明明白白的同学参考。1. 面试官的命题逻辑分布式锁到底在考什么1.1 为什么一道分布式锁题能区分出背题和会做分布式锁在Java后端面试里几乎成了标配题但它被问得这么频繁不是因为面试官想考你Redis命令背得多熟而是这一道题能把四块知识全部串起来Redis的使用能力、Java并发编程的功底、分布式系统的基础理论、以及高可用架构的设计意识。我面过的人里最常见的回答是这个版本用SETNX加锁用完后DEL释放。如果简历上写了Redis分布式锁但回答止步于此那几乎等于告诉面试官——你只是用过没踩过坑。而真正被认可的答案通常会把这三个层次讲清楚第一层是能不能把锁加上第二层是加锁之后异常了怎么办第三层是Redis本身不可靠时怎么办。这也是面试官不断追问的动力来源。他会顺着你的回答往深挖加了过期时间业务没执行完锁过期了怎么办释放锁之前怎么保证不会误删别人的锁主从切换的时候锁丢了还能怎么办每一问都是在探测你到底是在背题还是真的理解这套机制背后的取舍。1.2 从项目里的一句话到五层考察地图绝大多数人分布式锁都不是凭空学的而是项目里用到了大部分是订单防重复提交或者定时任务多实例调度这种场景。问题在于很多人只停留在会用RedisTemplate写一个加锁工具类的程度对面试官来说这句话是最好撕开的口子。我把面试官可能的追问路径整理成五层基本覆盖了100道题的知识面层次考察内容典型追问概念层什么是分布式锁、为什么本地锁不行你们为什么不用synchronized或ReentrantLock实现层Redis命令的正确姿势、原子性保证SETNX和SET EX NX有什么区别为什么必须Lua工程层Redisson封装、看门狗、可重入watchdog续的是什么锁时间设多少合适一致性层主从切换、RedLock、ZK/etcdRedis主从切换锁会丢吗RedLock靠谱吗架构层锁粒度、幂等设计、性能取舍秒杀库存能用一把锁解决吗高并发下怎么设计你会发现大多数人的知识储备卡在第二层和第三层之间。能说出SET key value EX 10 NX和Lua脚本已经开始领先能聊Redisson内部原理已经进入中上水平能把RedLock的争议、ZK/etcd的适用边界讲清楚这才是高级岗位期望看到的深度。1.3 100道题怎么分布刷题顺序怎么安排这套题库我按实际面试出现频率排了序。第一模块是基础概念20道几乎必考第二模块是Redis实现细节20道必考中的核心第三模块是锁粒度与性能20道中高级面试爱问第四模块是高可用与一致性20道用来拉开差距第五模块是场景综合20道资深工程师和架构师岗位的重灾区。刷题建议是先吃透模块一和模块二把Redis命令、Lua脚本、Redisson原理记牢然后直接跳到模块五的场景题因为场景题能帮你把前两个模块的知识串成体系。模块四争议性较强适合作为拔高内容不建议一上来就啃RedLock的世纪论战。至于用豆包这类AI工具刷题我保留一个态度它适合帮你生成追问、检查答案逻辑是否闭环但最后一定要回到源码和实测否则你背下来的只是AI替你总结的话术。2. Redis加锁的经典演变SETNX、原子性与误删锁的坑2.1 如果让你设计一个分布式锁第一版会怎么写这是模块二里最经典的一道开胃题。很多人一口就能回答用SETNX但面试官真正想看的是你从能跑到正确的演进过程。最原始的版本是单条SETNX lock order 1如果返回1表示拿到锁返回0表示别人持有。问题非常明显客户端拿到锁之后如果突然宕机锁永远不释放其他请求全部被卡死。所以第一版正确的演进是加过期时间SETNX设置成功后再调EXPIRE lock 10。但这里又有坑——这两条命令不是原子的。SETNX执行成功之后、EXPIRE执行之前进程挂掉锁照样死锁。所以标准的答案是直接使用Redis的原子命令SET lock orderId EX 10 NX。一条命令同时完成不存在才设置和设置过期时间两件事。这里我会顺便提一句value不能随便填必须是一个能标识当前请求的唯一值通常是UUID加线程ID的组合这样后面的释放操作才能安全地校验归属。2.2 释放锁为什么非要用Lua脚本释放锁的坑比加锁更隐蔽。最直接的写法是DEL lock但如果当前线程的锁已经因为超时被自动释放了后续线程B拿到了锁此时线程A业务执行完一个DEL就把B的锁删了。这是典型的误删问题面试官几乎必问。正确做法是把比对value和删除key放进同一个原子操作里Redis原生没有这个复合命令所以必须用Lua脚本if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这段脚本的逻辑先取出锁的当前value和请求方携带的唯一标识比对一致才删除不一致直接返回0。为什么必须用Lua因为Redis从2.6开始对Lua脚本整体加锁执行整个脚本原子完成中间不会有其他命令插入。这个机制要重点记住后续很多题——比如可重入锁、分段锁、分布式限流——都用同样的思路扩展。2.3 看门狗到底续的是什么Redisson的watchdog机制面试追问到这里通常矛头会指向业务执行时间超过了锁的过期时间该怎么办。手动把过期时间设成30秒或者60秒是一种粗暴方案但超长业务无法预估不设置过期时间宕机死锁风险又回来了。Redisson的解法是watchdog。用Redisson加锁时如果你没有显式指定leaseTime客户端会启动一个后台定时任务默认锁时长为30秒每10秒检查一次如果锁还在持有中就把过期时间重置为30秒。这样业务线程只要活着锁就自动续期业务线程一旦宕机心跳停止锁最多撑30秒就自动释放。这里有两个细节面试官喜欢挖。第一watchdog只有在不指定leaseTime时才生效如果你手动传了leaseTimeRedisson认为你知道自己在做什么不会续期。第二为啥是30秒和10秒这个数值背后有讲究10秒续期一次意味着即使续期线程故障锁也最多残留20到30秒不会出现永久死锁属于工程上折中的选择。面试时如果能把这个数字的含义说清楚会很有说服力。2.4 模块二题单Redis实现细节20问题号题目一句话答案要点21SETNX和SET EX NX的区别SETNX不能原子设置过期时间SET EX NX一条命令完成22加锁和设置过期时间为什么要原子操作避免加锁成功后进程崩溃导致永久死锁23锁的value应该怎么设计唯一标识如UUID线程ID用于安全释放24释放锁为什么用Lua脚本比对value与删除key必须原子防止误删25Redis执行Lua脚本是原子的吗是脚本整体执行不受其他命令打断26锁过期时间设置多少合适不固定需覆盖绝大多数业务耗时必要时靠续期27业务执行超过锁过期时间怎么办续期、延长过期时间、异步检查业务是否结束28Redisson的watchdog原理默认30秒锁每10秒续期无leaseTime时生效29lock和tryLock区别lock无限等待tryLock支持等待时间与租约时间30waitTime和leaseTime怎么理解waitTime是抢锁等待上限leaseTime是持有锁时长的上限31Redisson加锁用的什么数据结构Hashfield存客户端标识value存重入次数32Redisson如何实现可重入同一线程重复加锁时重入计数加一33如何避免误删别人的锁校验value匹配后再删除整体放入原子操作34锁的key命名有规范吗建议biz:resource:id格式按资源维度区分35集群模式下SET命令可靠吗单节点可靠但主从切换时存在锁丢失窗口36Redis主从复制下锁会丢吗会主节点未同步到从节点即宕机时锁丢失37RedLock是什么向多个独立Redis实例申请锁过半成功才算获锁38RedLock有哪些争议依赖时钟、网络分区下无法严格保证互斥39为什么说Redis锁是AP而非CP保证可用性优先极端场景牺牲一致性40Redisson还有哪些锁读写锁、联锁、信号量、公平锁、红锁3. 锁粒度与并发性能从全局一把锁到分段锁3.1 为什么不能一个系统用同一个key加锁我第一次见人把整个订单模块的key设计成lock:order的时候心里就在叹气。这样加锁等于把所有用户的订单操作全部串行化下单、支付、取消全部排队吞吐量直接崩塌。分布式锁本质上是让互斥资源按需串行但锁的粒度必须和你要保护的资源匹配。资源维度是什么锁key就应该是什么。比如要保护库存key设计成lock:inventory:{skuId}不同商品互不影响要防止同一个用户重复下单key设计成lock:order:{userId}不同用户也不会互相阻塞。这个点几乎是白送的送分题但就是有人答不好。面试时如果简历上有高并发项目这里一定会被追问你的key是按什么维度拆的拆完之后有没有压测验证过不同用户之间真的不互斥了。3.2 常见误区库存扣减到底该不该用分布式锁库存扣减是我见过的最容易把概念讲拧巴的场景。很多人一上来就说用分布式锁保护扣库存然后整个服务就出现了严重的性能瓶颈。其实库存扣减这个动作本身如果用Redis做完全不需要分布式锁一条DECR inventory:{skuId}就是原子的如果用MySQL做UPDATE inventory SET stock stock - 1 WHERE sku_id ? AND stock 0也是原子操作。原子操作能解决的就不该引入锁。那分布式锁在库存场景里到底用在哪通常是用在需要先查库存再决定是否扣减这种复合操作上比如先判断库存是否充足、再扣减、还要写一条库存流水这三步必须作为一个整体。锁住的是那个复合操作而不是扣减那一行。如果面试官问你秒杀库存怎么设计你可以从这条线展开Redis Lua原子扣减-发送MQ异步落库-数据库唯一键兜底。锁不是主角削峰和兜底才是。3.3 面临热点商品分段锁是怎么回事即使把锁粒度拆到sku维度某些爆款商品仍然扛不住。一件商品一上架所有流量都打向同一个lock:inventory:{skuId}这个key成了全局热点分布式锁的自旋、等待、唤醒都在这个key上竞争。这就是引入分段锁的原因。分段锁的思路是把资源分成N份比如库存1000件拆成10段每段100件段有独立的锁key。请求根据某种规则——比如用户ID哈希取余——落到某个段上只锁这个段。段内库存扣完了再尝试下一个段或者直接返回失败。这样并发度提升了N倍代价是逻辑复杂度上升可能出现某段空了但其他段还有货的情况。实际工程里分段数、段间库存转移策略都比理论上复杂而且如果库存总量很低分段反而增加开销。所以分段锁最适合的是库存量大、并发极高、允许少量请求失败的场景属于典型的牺牲强一致性换吞吐量的取舍。这里我补充一个容易被忽略的点分段锁通常和库存预扣减配合使用而不是作为唯一的库存控制手段。分段逻辑只解决Redis层面的热点竞争最终数据库扣减还是要靠行锁或乐观锁保证不超卖。面试时讲清楚这种分层设计比只记住分段锁三个字有说服力得多。3.4 模块三题单锁粒度与性能20问题号题目一句话答案要点41为什么不能用同一个key锁所有请求全局串行化吞吐量崩塌锁粒度与资源不匹配42如何设计细粒度锁key按业务资源维度拆分biz:resource:id43锁粒度太细有什么缺点锁数量膨胀、管理复杂、某些场景反而降低效率44什么是分段锁资源拆多段各段独立锁key分散热点竞争45分段锁如何解决数据倾斜哈希不均匀时查表映射或动态扩缩段46库存扣减为什么容易超卖查库存和扣库存不是原子操作并发竞态导致47减库存先锁还是先扣能用原子操作解决就不加锁锁保护的是复合操作48Redis原子扣减和分布式锁的关系DECR本身原子无需锁锁用于跨操作的一致性保证49同步扣库存和异步削峰怎么配合Redis扣减成功-MQ异步落库-数据库兜底50分布式锁和本地锁可以结合吗可以本地锁挡住单机内竞争分布式锁跨节点互斥51什么是自旋等待拿不到锁时循环重试需要控制次数和间隔52锁等待超时怎么办返回失败、降级、走异步重试队列53如何压测分布式锁性能关注TPS、平均等待时间、P99耗时、锁丢率54锁竞争激烈时怎么提升吞吐拆分锁粒度、分段锁、减少临界区范围55读写锁适合什么场景读多写少的资源如配置项、白名单56什么时候不需要分布式锁原子操作可解决、允许最终一致、无并发写竞争57Redis和数据库数据一致性怎么保证先更新DB再删缓存、Binlog订阅、补偿任务58缓存击穿和锁有关系吗可以用互斥锁控制热点key的重建但需防死锁59用Redis锁保护热点key重建行不行可以但更推荐逻辑过期或分布式锁多级缓存60高并发下锁和队列如何取舍锁保护一致性队列控制流量两者解决不同问题4. 高可用与一致性之争主从切换、RedLock与ZK/etcd4.1 Redis主从复制锁丢失的机制要想明白我在模拟面试里反复追问的一个点是你的Redis锁是部署在单机、哨兵还是Cluster模式这三个模式的可靠性边界完全不一样。单机模式下Redis宕机锁就没了所有业务错误地以为自己还能持锁互斥失效。哨兵模式解决了自动故障转移但引入了新的问题主节点写入锁成功这条数据还没有异步复制到从节点主节点就挂了哨兵把从节点提升为主节点此时锁不存在了。客户端B就能成功加上同一把锁两个客户端同时进入临界区。Cluster模式类似数据是分片的但每个分片内部依然存在主从异步复制延迟锁丢失问题本质一样。这个问题没有完美的Redis纯软件解法。业界有一些补偿思路比如写入后同步等待从节点确认或者把锁的有效期和复制延迟强关联但都无法从根本上消除窗口。面试时能准确说出异步复制导致锁丢失这样的机制并指出这是Redis分布式锁在AP模型下的天然短板已经可以超过大多数候选人。4.2 RedLock推导过程与争议别只说好或不好RedLock的基本思路是既然单台Redis不可靠那就向N个独立的Redis实例申请锁只有拿到超过半数实例的锁才算成功。释放时向所有实例广播释放命令。这个设计降低了单点失败导致锁丢失的概率代价是复杂度上升、需要部署多套独立Redis而且引入了新的理论争议。争议主要来自分布式系统领域。有人在《How to do distributed locking》里指出RedLock不能严格保证互斥性依赖系统时钟如果某个实例的时钟发生跳跃锁的有效期可能被意外缩短或延长网络分区时客户端可能在某些实例上拿锁成功、另一些失败而获锁过半的客户端与认为锁已过期的客户端同时存在两个客户端依然可能同时进入临界区。面试时怎么答这道题我的建议是别急着站队。先客观陈述RedLock的适用场景——对可用性要求极高、可以接受额外的机器成本、且能容忍极小概率的互斥失效再指出争议的核心——分布式锁要的是严格互斥而Redis系的AP模型无法在不引入强一致组件的前提下给出数学上的严格保证。最后落到工程实践关键业务我用ZooKeeper或etcd非关键路径用Redis锁降低复杂度。这个答案有观点、有依据、有边界。4.3 想要更可靠的锁ZooKeeper和etcd的答案如果需要更严格的互斥保证ZooKeeper和etcd是比Redis更合适的选择。ZK分布式锁的经典实现是临时顺序节点加Watcher客户端在锁节点下创建临时顺序节点检查自己是不是最小序号不是就监听前一个节点的删除事件。临时节点的特性保证了客户端宕机后节点自动消失锁自动释放不会出现Redis那种锁没续期就被别人拿走的状态。etcd的实现思路类似但更现代每个抢锁请求通过事务创建带revision的key持有最小revision的客户端获得锁配合lease租约机制客户端需要持续续租租约过期则key自动删除。etcd的revision天然给了锁一个全局递增序号实现公平锁比Redis简单得多。三个方案怎么选面试官期待的是一张清晰的对比对比项RedisZooKeeperetcd一致性模型AP最终一致CP强一致CP强一致锁实现复杂度低命令Lua中节点Watcher中租约revision性能高内存操作中节点多跳较高Raft协议故障处理过期时间兜底临时节点自动消失租约超时自动清理典型场景高并发、允许极小概率失效对一致性要求高的业务云原生环境、配置中心注意这里有个细节ZK为什么能比Redis更可靠因为ZK用的是ZAB协议写操作需要过半节点确认才返回成功锁不存在主节点已写、从节点没同步的窗口。etcd的Raft同理。代价是写入延迟更高吞吐量不如Redis。所以工程上真正的判断标准是业务能不能容忍那个极小概率的锁丢失而不是无脑追捧CP系统。4.4 模块四题单高可用与一致性20问题号题目一句话答案要点61分布式锁的高可用怎么保证故障转移、多副本、锁自动过期兜底62Redis哨兵模式下锁会丢吗会主从异步复制存在丢失窗口63主从切换过程中锁丢失如何处理损后补偿、短锁时间、关键业务换CP组件64RedLock的完整实现步骤多实例加锁、过半成功、释放广播65RedLock依赖时钟吗依赖各实例本地时钟判断锁过期存在时钟跳跃风险66有人怎么评价RedLock认为无法严格保证互斥推荐幂等保护等其他方式67ZK分布式锁的原理临时顺序节点监听前驱节点最小序号获锁68ZK临时节点和持久节点区别临时节点会话结束自动删除持久节点需手动清理69ZK客户端断分会怎样会话超时后临时节点删除锁可能被其他客户端获取70etcd实现锁和Redis有何不同etcd基于revision排序和lease租约天然公平71什么是lease租约给key绑定生存期的机制需要定期续约72什么是revisionetcd中每次修改的全局单调递增版本号73为什么ZK/etcd是CP系统写入需多数节点确认牺牲部分可用性换取一致性74选ZK还是etcd云原生选etcd已有ZK维护体系选ZK看团队运维能力75分布式锁和分布式事务的关系锁解决并发互斥事务解决多资源原子提交76什么是脑裂网络分区导致多个节点以为自己是主锁无法全局唯一77锁服务本身单点怎么办部署集群、使用CP中间件、监控关键指标78如何评估锁可用性SLA记录故障次数、最长不可用时间、锁丢失率79业务对一致性极高要求怎么办避免分布式锁改用数据库行锁、串行化、幂等设计80锁自动续期最坏情况业务卡死但看门狗持续续期锁长时间不被释放5. 场景题专项秒杀、防重提交与定时任务调度的正确设计5.1 秒杀库存扣减锁应该放在哪个环节秒杀是场景题里最常考的也是最能暴露候选人是否理解锁的定位。常见错误设计是所有请求进来先抢一把全局锁抢到锁之后查库存、扣库存、创建订单全程串行。这样做的结果就是Redis和数据库都被压垮秒杀变成慢杀。秒杀的正确设计思路是分层削峰锁只在必要的边界出现。最常被接受的方案请求进入后直接用Lua脚本对Redis库存执行原子扣减扣减成功才允许进入下游同步发送MQ消息由消费端异步创建订单、扣减数据库库存数据库层用唯一索引兜底防超卖比如订单号加商品ID的唯一约束。整个链路中如果一定要用到锁它保护的不是所有请求而是同一用户的重复请求或者订单创建的临界资源。这个方案的关键在于把互斥范围缩小到极致Redis的DECR本身就是原子操作不需要锁MQ的异步化把同步压力打散数据库唯一索引从数据层面保证不重复。面试官如果在秒杀题里追问锁怎么设计你可以明确告诉他好的秒杀设计不是锁多而是锁少、锁得准。5.2 接口防重提交锁不是最优解幂等才是防重提交这个场景很多人第一反应就是分布式锁但实际业务里这是一个典型的幂等设计优于锁设计的场景。设想用户快速点了两次提交订单按钮发起了两个相同的请求。如果用分布式锁第一个请求加锁执行第二个请求等待锁、拿锁、执行结果是两个请求都会创建订单锁挡不住重复创建除非你还在锁内做了一次是否已存在的判断。更稳妥的方案是幂等。给每个请求带上一个唯一的业务请求号数据库给请求号加唯一索引重复请求在数据库层就会被拦下并返回第一次执行的结果。如果交互流程允许前端生成唯一Token、后端校验Token是否已消费也可以做到同样的效果。锁在这个场景里的定位是同一用户同时提交多个不同请求时的互斥比如一个用户同时提交了两个不同金额的订单业务要求同一时间只能处理其中一个这才轮到分布式锁上场。我觉得这两者的区别值得反复讲因为面试官问接口防重想听的不是你会用锁而是你能分清什么时候锁有用、什么时候锁没用。5.3 分布式定时任务ShedLock、数据库行锁与任务分片分布式定时任务多实例部署后同一时刻不应有两个实例执行同一个任务。最简单的方式就是在任务执行前抢一个分布式锁抢到了才执行。业界还有个专门的框架ShedLock就是为这个场景设计的。它的原理和Redis锁很接近一个内置的表或Redis key记录任务锁的持有者和过期时间执行前检查是否被其他实例持有。另一个思路是用数据库锁。SELECT ... FOR UPDATE SKIP LOCKED可以做到只锁定没有被其他事务占用的记录适合从数据库里批量拉取待处理任务。这个方案的好处是不需要额外引入中间件坏处是数据库压力上升、锁等待受事务时间影响。如果任务量特别大分片会比锁更有效。ElasticJob这类框架会把数据按分片ID分给不同实例每个实例只处理属于自己的分片天然并行且不冲突。面试时把这三种方案按数据量从小到大排开讲是很出彩的回答小数据量用分布式锁中数据量用数据库行锁大数据量用分片。5.4 模块五题单场景综合20问题号题目一句话答案要点81秒杀场景怎么设计锁Redis Lua原子扣减MQ异步落库DB唯一索引82下单防重怎么实现唯一请求号数据库唯一索引优于分布式锁83分布式定时任务如何避免重复执行ShedLock、数据库行锁、分片策略84数据幂等和锁如何结合幂等解决重复请求锁解决并发互斥85同一个用户并发下单怎么处理按userId加锁锁内校验订单状态86用户积分解并发加错怎么办乐观锁或数据库原子更新少用分布式锁87库存预占和扣减要分开吗预占减Redis库存确认订单再减DB库存需补偿88高并发抽奖怎么设计锁只保护中奖资格发奖走异步写队列89多人协作编辑的锁怎么做CRDT或版本号比对全局锁体验很差90分布式文件锁有什么特殊考虑NAS并发、租约续期、断线重连91订单状态流转为什么用乐观锁状态机版本号比全局锁更简单直接92如何让锁自动过期但业务安全合理leaseTime看门狗业务完成检查93配置中心变更如何避免重复推送配置版本号Watch通知天然幂等94限流和锁是一回事吗限流控制速率锁保证互斥目标不同95分布式锁在压测中如何观察关注锁等待时间、抢锁成功率、吞吐下降比例96如何给面试官讲清楚你的锁方案从业务背景说到方案选型最后讲坑与验证97锁失败后的降级策略返回失败、排队重试、走本地限流保护98多级缓存锁如何设计本地锁先挡分布式锁跨节点兜底99分布式锁能保证事务最终一致吗不能它只管并发互斥一致性靠事务与补偿100从零设计一个分布式锁组件要考虑哪些API设计、续期、可重入、监控、多存储后端6. 代码级追问可重入锁、公平锁与手写一个最小实现6.1 可重入锁同一个线程递归加锁为什么必须支持面试官在Redis锁的题上进一步追问时很可能问你的锁支持可重入吗。可重入的含义是同一个线程在已经持有锁的情况下再次进入加锁方法不会被自己的锁挡住。如果锁不支持可重入一个方法里调用了另一个同样加锁的方法就会死锁——第二次加锁永远等不到第一次释放。比如订单服务里一个事务方法加了锁方法内部又调用了另一个加锁的Service方法这种情况在真实工程里非常常见。Redisson的可重入实现用的是Hash结构锁key对应一个Hashfield是客户端唯一标识value是重入次数。加锁时如果field已存在且属于当前线程就把value加一释放锁时先减一减到0才真正删除整个key。这个设计的妙处在于重入次数的维护也是原子操作依然可以通过Lua脚本完成。面试如果能画出来Hash结构和重入计数的变化过程基本就是满分答案。6.2 公平锁与非公平锁什么场景需要排队Redis的SET EX NX本质上是非公平锁谁抢到算谁的后来的请求可能反复抢不到。Redisson提供了RedissonFairLock实现公平排队原理类似信号量每个等待请求按到达顺序排队持有锁的线程释放后按队列顺序授予锁。ZooKeeper的临时顺序节点天然是公平的因为节点序号就是排队序号。etcd的revision也天然公平。公平锁的代价也很明显排队意味着等待时间更长、吞吐更低而且队列本身也需要存储和维护。实际业务里大多数场景不需要公平性比如订单锁、库存锁只要互斥就行谁先抢到无所谓。真正需要公平锁的场景通常是资源有限且有先后要求的比如集中开闸时放行顺序、限量发放的优惠券。面试时能主动说出我一般默认非公平锁特定场景才换公平锁会显得你有真实的工程判断。6.3 最小可用实现从加锁到续期到释放的完整闭环为了展示你真的理解整条链路不妨现场写一个最小实现的核心流程。以下是我在面试中常用的一套伪代码覆盖了加锁、续期、释放的关键路径// 加锁SET key value EX leaseTime NX public boolean tryLock(String key, String clientId, Duration leaseTime) { String script return redis.call(set, KEYS[1], ARGV[1], EX, ARGV[2], NX); Object result redis.eval(script, List.of(key), List.of(clientId, leaseTime.getSeconds())); if (result ! null OK.equals(result.toString())) { startWatchdog(key, clientId, leaseTime); return true; } return false; } // 续期仅在key对应value仍等于本线程标识时延期 public void renewExpire(String key, String clientId, Duration leaseTime) { String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(expire, KEYS[1], ARGV[2]) else return 0 end; redis.eval(script, List.of(key), List.of(clientId, leaseTime.getSeconds())); } // 释放比对value一致则删除 public void unlock(String key, String clientId) { String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; redis.eval(script, List.of(key), List.of(clientId)); }写这段代码时有四个细节会被面试官抓住第一加锁的value必须携带线程唯一标识不能只穿一个常量第二续期和释放必须在判断value归属后执行不然会误操作别人的锁第三续期逻辑里过期时间要基于原leaseTime重新设置而不是累加第四请求方在finally里释放锁是基本纪律这个以前吃过大亏——Redis客户端抛异常会导致锁没有释放排查半天才发现是release没写在finally里。6.4 关于Redis不可用、面试追问与一点个人建议最后分享一个被追问过一次、值得提前准备的连环题如果用户量暴涨Redis集群整体不可用了你的分布式锁怎么办好的分层回答是第一层本地JVM锁兜底虽然在多实例下不能完全互斥但能挡住绝大多数单机内重复请求第二层数据库唯一键或行锁兜底保证关键数据不产生脏数据第三层快速失败并打日志让业务感知降级而不是无限等待阻塞。这样即使Redis挂了系统也不会雪崩只是极端情况下可能出现小概率重复处理再靠幂等兜住。我用豆包把这些分布式锁的面试题全部过过一遍有一个很深的体会AI可以帮你把题面整理得井井有条可以告诉你RedLock争议双方各有什么论点可以在你回答之后立刻追问下一个问题但它没办法替代你去看一眼Redisson的源码也没办法替你在压测环境里验证那把锁在真实流量下到底丢没丢。分布式锁这道题从背住SET EX NX到真正理解什么时候不该用锁中间隔着的是踩坑的次数和看源码的耐心。希望这份百题梳理能帮你少走一段弯路。