想象你经营一个只有五个车位的网红停车场。每天早高峰,几十辆车同时涌进来抢同一个“黄金车位”——车位背阴、离电梯近、还有人专门帮你擦玻璃。问题是:这个黄金车位一次只能停一辆车,谁来停?停多久?万一停车的人临时离开了,别的车是干等着,还是直接把他的车拖走? 分布式锁,就是这些“抢车位规则”的计算机版本。在分布式系统里,无数台服务器(车)会同时争抢一份共享资源(车位)——可能是数据库里的一行库存、一个订单号,也可能是一个 GPU 训练任务。如果没有规则,两辆车同时停进同一个车位,就会发生“超卖”“重复扣款”这类事故。 这篇文章要讲三种主流的“停车场管理方案”:Redis 的“反光锥占位”、Redlock 的“连锁停车场五店同放”、ZooKeeper 的“智能取号排队”。它们各自用什么招数防止死锁、怎么应对故障、在什么场合会掉链子——读懂这些,你就能在架构设计中做出靠谱的选择。
分布式锁是微服务与高并发架构的“安全气囊”。掌握 Redis/Redlock/ZK 三种方案的原理与边界,能让你在架构评审、故障排查中一眼看穿死锁与超卖风险,这是分布式工程师和架构师的核心基本功。
目录
可视化演示
展示Redis SET NX PX加锁流程,Redlock多节点投票获取多数锁,以及Redis/ZK/Etcd三种分布式锁实现的性能对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
加锁流程 SET NX PX NX+PX=互斥+过期 仅当key不存在时设置成功,保证互斥并自动过期。
锁的问题本质
那个黄金车位每天早高峰都有两辆车同时抵达。如果管理员不在,两辆车就都挤进去——车门刮花、出口堵死。在计算机里,这个“黄金车位”就是临界资源:数据库里的一行记录、一个计数器、一个任务配额。多个线程或服务同时读写它,就叫竞态条件,结果往往是数据错乱。
单机时代,Java 的 synchronized 或操作系统锁就能搞定——因为所有线程共享一块内存,锁状态写在哪里大家都看得见。但到了微服务时代,抢车位的“司机”分布在不同的服务器上,谁也不认识谁,无法靠一块共享内存沟通。于是我们需要一把“看得见、摸得着、放在公共区域”的锁。
分布式锁必须满足三个基本要求:互斥性(同一时刻只有一个客户端持有)、防死锁(持有者崩溃后锁能自动释放)、可重入(同一客户端可重复获取)。这三点分别对应“车位上只有一辆车”“车主消失后管理员能清理”“自己人回头再停不用重新排队”。
所以,分布式锁本质上是在“没有共同管理员”的停车场里,设计一套能让所有车互相信任的占位规则——这套规则怎么设计,决定了 Redis、Redlock、ZK 三种方案的命运。
- 互斥性(M)任一时刻至多一个客户端持有该锁,否则两个线程同时写资源必然出错。
- 防死锁(D)持有者崩溃或网络断开后,锁必须在有限时间内自动释放。
- 可重入(R)同一线程在持锁期间再次申请同一把锁时,不会被自己挡住。
Redis基础锁:反光锥与荧光定时贴
最简单粗暴的方案:管理员在黄金车位上放了一箱荧光锥。每辆车要停,先跑到车位边问一句:“有锥吗?”没有,就把贴着自己车牌号的锥放上去,停车;有,就在旁边等。
Redis 的 SETNX 就是这个“放锥”动作——SET if Not eXists。执行 SET key value NX,如果 key 不存在就设置成功并返回 OK,相当于锁被抢到;如果 key 已存在就返回空,相当于车位已被占。value 放什么?放一个全局唯一的客户端标识(比如 UUID),就像锥上贴的车牌号。
但进程可能中途崩溃,锥一直放那儿,后面的车永远停不了——这就是死锁。所以必须给锥贴一张“荧光定时贴”:TTL(过期时间)。到期了锥自动消失,锁自动释放。注意:SETNX 和 EXPIRE 如果分两条命令执行,中间进程一崩,定时贴就没了。正确做法是用 SET key value NX EX 30 一条命令搞定,或者用 Lua 脚本保证原子性。同理,释放锁不能直接 DEL——要先比对 value 是不是自己的车牌,再用 Lua 保证“比对 + 删除”原子,否则会把别人的锥扔掉。
荧光锥加定时贴,解决了“谁占位”和“占多久”两个问题。但 Redis 毕竟只是一个停车场——主库挂了怎么办?定时贴谁来撕了重贴?这两个问题,把我们带向看门狗和连锁停车场。
- SET 命令Redis 的基础写命令,这里同时携带三个选项,一次网络往返完成。
- NX 条件仅在键不存在时设置——这是互斥性的判断开关。
- EX 过期同时设置 TTL,到期自动删除——这是防死锁的开关。
- 原子性NX 与 EX 同属一条命令,Redis 单线程执行,命令不可拆分。
看门狗续约:撕掉旧贴纸,贴上新贴纸
荧光定时贴只能烧 30 秒,可你的车要做大保养,需要 2 分钟。难道贴纸一烧完,后面的车就能把你的车拖走?停车场老板想了个办法:雇一条狗盯着贴纸,快烧完就撕掉重新贴一张——只要你还在车位上,贴纸就永远续上。
这条狗就是看门狗(Watchdog)线程。以 Java 的 Redisson 客户端为例:默认锁的 leaseTime 是 30 秒,拿到锁后看门狗每 10 秒检查一次,如果锁还在持有中,就用 Lua 脚本把过期时间重置回 30 秒。业务线程还没干完,锁就不会提前到期。
这里有个关键权衡:过期时间设多长?设太长,持有者真崩了,其他服务要干等很久;设太短,业务稍微抖动,锁就提前释放,另一个线程进来同时写资源。业界实践是“业务平均耗时的 3-5 倍 + 看门狗续约”,而不是把 TTL 压到极限。另外别忘了主从复制的坑:Redis 主库收到 SETNX 后异步复制给从库,如果主库在复制完成前宕机,从库升主后锁就凭空消失了——单点 Redis 锁在“主挂”场景下会丢锁。
看门狗让“流量高峰不丢锁、持有者崩溃不卡死”成为可能。但狗再敬业,也挡不住主库突然塌了——这时候就得考虑把停车场开成连锁店了。
- 剩余时间判断当锁的剩余有效期不足 TTL 的一半时,看门狗开始行动。
- 续约动作将过期时间重置为当前时间加上完整 TTL,相当于撕掉旧贴纸、贴上新贴纸。
- 前提校验续约前必须校验 value 仍是自己的标识,防止给别人的锁续约。
Redlock算法:连锁停车场的多数投票
单店停车场最大的风险:主收银台的账本丢了,所有“谁占着车位”的记录全没了。连锁停车场老板想了个办法:把同一个黄金车位在 5 家分店里各登记一次,只有至少 3 家分店都确认“这个车位归你”,你才真正获得车位。一家分店的账本烧了,还有两家记得。
这就是 Redlock 算法的思路。它在 5 个互相独立的 Redis 节点上,用同一个 key 和同一个随机 value 执行 SET NX EX。客户端依次尝试 5 个节点,统计成功的数量,同时记录总耗时。成功条件有两个:成功节点数 ≥ 3(大多数),并且总耗时小于锁的有效期——否则你这边刚凑够 3 票,锁已经过期了,等于拿着过期的停车券停车。
Redlock 有个著名争议:它假设各节点时钟是同步的,但现实世界会时钟漂移(NTP 校准、手动调时间),节点 A 觉得过期时间还剩 10 秒,节点 B 觉得已经过了 5 秒。另外,持有锁的进程如果发生长时间的 GC 停顿(JVM Stop-The-World),停顿期间锁到期被别的进程抢走,恢复后原进程还以为自己持着锁——这就是 Martin Kleppmann 与 Redlock 作者 Antirez 之争。实践中的补充方案是隔离令牌(fencing token):每次成功获取锁时递增一个序号,写数据时带上这个序号,下游存储拒绝旧序号的写入,就像“停车券上印着第几批,错过批次的券一律不认”。
连锁停车场用“多数分店记忆”对冲了单店账本丢失,但它仍然依赖所有分店的时钟都准。没有完美方案,只有把风险窗口压到可接受的范围。
- 法定多数5 个节点要取超过一半才算数,计算可得至少 3 个。
- 票数条件成功写入的节点数必须达到法定多数。
- 时间条件从开始尝试到选票结束的总耗时,必须小于锁的有效期。
ZK临时顺序锁:智能取号机与接力棒
第三家是智能停车场:不用反光锥,改在入口放一台取号机。车到了先取号,号码小的先进。更妙的是,服务台和你的手机连着——如果超过约定时间联系不上你(会话断了),系统自动把你排的号作废,后面的车依次顶上。绝对不会出现“人走了号还占着”的尴尬。
ZooKeeper 提供两种节点特性,组合起来正好是分布式锁:临时节点(EPHEMERAL)——客户端会话断开时自动删除;顺序节点(SEQUENTIAL)——创建时自动附加全局递增序号。在同一个锁目录下创建临时顺序节点,序号最小的节点就是锁的持有者。其他节点只需要监听自己前一个节点的删除事件,前一个消失,就轮到自己了。
ZK 锁有个经典陷阱叫羊群效应:如果所有排队节点都去监听“最小节点”被删除,一旦锁释放,所有排队的进程同时被唤醒,大家一起往 ZK 上写请求——瞬间打爆集群。正确做法是每个节点只 watch 自己前面的那个节点,像接力棒一样逐个传递。此外,临时节点的生命周期绑定会话(session),如果持有者 GC 停顿导致心跳超时,会话过期,锁也会被自动清理——这一点和 Redis 锁的“过期”殊途同归。但 ZK 的优势在于:底层是 ZAB 协议 + 半数落盘,不存在 Redis 主从异步复制那种“主挂丢锁”问题。
智能停车场用“取号 + 会话心跳”换来了公平排队和强一致,代价是每次取号都要找服务台登记、更新队列——慢。这就引出了三种方案怎么选的问题。
- 最小序号持锁所有等待者中序号最小的节点成为锁持有者。
- 顺序监听每个节点只 watch 前驱节点,避免所有节点同时被唤醒。
- 前驱删除前驱释放或被清理后,当前节点顺势获取锁并执行业务。
方案选型对比:没有最好的停车场
三个停车场摆在你面前:路边摊式 Redis 便宜快速但账本单薄;连锁式 Redlock 稳妥可靠但要多店协调;智能式 ZK 公平有序但进门排队久、服务台设备贵。选哪个,取决于你的车有多贵、堵车能忍几分钟。
从数据面看:Redis 单机锁性能最高(微秒级),但一致性最弱,主备切换可能丢锁;Redlock 需要 5 次网络往返加多数判定,性能约为 Redis 的 1/5,抗单点故障更强,但引入了时钟依赖;ZK 每次创建顺序节点都要跨 ZAB 半数落盘,吞吐量通常是几百到几千 QPS 级别,远低于 Redis,但强一致性最让人安心。
更重要的判断维度是业务需求:可容忍“极端情况下锁失效、靠幂等兜底”→ 选 Redis;需要“跨机房容灾 + 不依赖单点”→ 选 Redlock;要求“强一致、严格公平排队、不可丢锁”且并发量不高 → 选 ZK。没有银弹,选型就是给“性能、一致性、运维成本”三个指标打分。
选型没有“最好的停车场”,只有“最适合你车况的停车场”。想清楚自己最不能承受哪种事故,答案自然浮现。
- Redis 分支性能优先、可容忍小概率丢锁的业务,选单机 Redis。
- Redlock 分支单点已不可接受,愿意为多数投票付出 5 倍网络开销。
- ZK 分支一致性压倒一切,接受较低的写入吞吐。
生产实践与故障复盘:锁失效后的三道保险
停车场建好了,规则也定了,但真正考验管理员的是一次真实的早高峰事故。有一辆车停进黄金车位后突然爆胎(进程卡死),看门狗没来得及续约,锁过期了,另一辆车停了进来。两辆车同时占位——处理问题的关键从来不只是“锁”,而是“锁失效后怎么办”。
这正是生产实践的核心:分布式锁不是万能的,必须配上三道保险。第一道是锁粒度:能锁“订单号”就不要锁“整个订单表”,能把库存按 SKU 分段锁就不要一把大锁锁死所有商品,锁粒度越细,并发度越高。第二道是超时降级:执行业务前设定整体超时,超过兜底时间就放弃或走降级路径,避免无限等待。第三道是幂等兜底:即使锁真的失效了,重复执行带来的写入也要能自洽——比如扣库存用“库存 ≥ 数量”条件更新,下单用唯一订单号防重,让数据库成为最后一道防线。
举个真实复盘案例:某电商平台做秒杀,最初用 Redis 单锁 + 只看 TTL,大促时主库抖动导致主从切换,锁全部丢失,库存被两个服务同时扣减,出现超卖。复盘后的方案是:Redis 锁 + 看门狗续约 + 扣减用条件更新(UPDATE stock SET num = num - 1 WHERE num >= 1)兜底 + 订单写唯一索引。四条措施叠加,即使锁失效,数据库的条件更新也会拒绝超卖。这就是“锁负责效率,数据库负责正确性”的经典组合。
停车场最重要的不是锥有多新,而是“出事之后 5 分钟内能不能把车挪走”——分布式锁同样如此。锁只是拦截并发冲锋的第一道闸门,幂等、超时、降级才是最终的安全网。
- 业务时长余量业务执行时间要显著小于 TTL,给锁留出安全余量。
- 兜底超时整体兜底超时要大于 TTL,保证“锁先释放,兜底后触发”,不会出现兜底线程还在等锁。
AI 应用场景
大模型训练任务调度:给 GPU 上锁
阶段:ML 平台 / 任务调度与资源管理
解决的问题:多个实例同时调度同一个 GPU 节点、重复拉起同一训练任务,会导致显存冲突或重复计费。传统方式是“人肉排期 + 数据库标志位”,并发一高就出现竞态。用 Redis SETNX 锁给每个 GPU 加锁,任务持有锁后才能分配资源,训练结束释放。
具体案例:某 AI 平台 100 个训练任务并发申请 8 张 A100,调度器用 Redlock 保证跨机房不重复分配。锁 value 中写入任务 ID 和隔离令牌,模型保存时校验令牌,避免旧任务把新任务的 checkpoint 覆盖。
特征平台在线更新:让特征版本排队发布
阶段:特征工程 / 在线特征库写入
解决的问题:多个特征回刷任务同时更新同一特征版本,会写坏线上 Serving 缓存。ZK 临时顺序锁让回刷任务按序排队,强一致保证特征版本切换不跳跃。
具体案例:某推荐系统午夜做特征大版本更新,40 个回刷 Job 同时启动。用 ZK 顺序锁排队,Job 失败时会话断开自动清理,后面的 Job 依序执行,特征版本按序发布,线上推理无抖动。
大促秒杀库存扣减:预测流量后的并发保护
阶段:智能运维 / 容量预测后的高并发保护
解决的问题:大促前用 AI 预测模型算出峰值 10 万 QPS,库存扣减必须不超卖。单纯靠 Redis 锁不够,要配条件更新与唯一索引做幂等兜底。
具体案例:某电商 618 秒杀,Redis 锁因主从切换失效一次,但 UPDATE stock SET num=num-1 WHERE num>=1 拒绝了负库存,订单唯一索引拦截了重复提交,全程零超卖。
Python 演示
# 模拟三种分布式锁的核心语义:SETNX/TTL、Redlock 多数投票、ZK 顺序排队import uuid class RedisLock: """模拟 Redis SET value nx ex 语义的锁""" def __init__(self, ttl=5.0): self._store = {} self.ttl = ttl def try_acquire(self, key, owner, now): if key not in self._store or self._store[key][1] <= now: self._store[key] = (owner, now + self.ttl) return True return False def renew(self, key, owner, now): if key in self._store and self._store[key][0] == owner: self._store[key] = (owner, now + self.ttl) return True return False def release(self, key, owner): if key in self._store and self._store[key][0] == owner: del self._store[key] return True return False class Redlock: """5 个节点中至少 3 个成功才算持锁""" def __init__(self): self.nodes = [RedisLock(ttl=2.0) for _ in range(5)] def try_acquire(self, key, owner, now): ok = 0 for node in self.nodes: if node.try_acquire(key, owner, now): ok += 1 return ok >= 3 def release(self, key, owner): for node in self.nodes: node.release(key, owner) class ZkLock: """序号最小的排队者持锁,前驱释放后继续推进""" def __init__(self): self.queue = [] def enqueue(self, owner): seq = len(self.queue) + 1 self.queue.append((seq, owner)) return seq def acquire_or_wait(self, key, owner): seq = self.enqueue(owner) return seq == 1 def release(self, key, owner): for i, (seq, o) in enumerate(self.queue): if o == owner: del self.queue[i] return True return False def demo(): owner = uuid.uuid4().hex lock = RedisLock(ttl=1.0) now = 10.0 print('Redis 获取锁:', lock.try_acquire('order:001', owner, now)) print('Redis 重复获取被拒:', lock.try_acquire('order:001', 'B', now)) now += 1.2 print('TTL 过期后 B 获取成功:', lock.try_acquire('order:001', 'B', now)) print('A 释放自己的锁失败:', lock.release('order:001', owner)) rl = Redlock() print('Redlock 五节点三票通过:', rl.try_acquire('gpu:0', 'C', 0)) rl.release('gpu:0', 'C') zk = ZkLock() print('ZK 第一位持锁:', zk.acquire_or_wait('slot', 'X')) print('ZK 第二位排队:', zk.acquire_or_wait('slot', 'Y')) zk.release('slot', 'X') print('ZK 释放后队列长度:', len(zk.queue)) if __name__ == '__main__': demo()
self._store[key] = (owner, now + self.ttl)模拟 Redis 写入键值并附带过期时间,等价于SET key value NX EX。if key not in self._store or self._store[key][1] <= now:键不存在或已过期才能获取,这就是 NX + TTL 的判定逻辑。if key in self._store and self._store[key][0] == owner:释放前校验持有者身份,防止误删别人的锁,对应 Lua 脚本的“比对 + 删除”。ok >= 3Redlock 的法定多数:5 个节点中至少 3 个成功才算持锁。seq = len(self.queue) + 1模拟 ZK 顺序节点的全局递增序号。return seq == 1最小序号者获得锁,其余返回 False 表示排队等待。del self.queue[i]模拟临时节点被删除后队列推进,下一辆车自动顶上。
常见误区
误区我用 Redis 的 SETNX 拿到 key,锁就到手了,从没想过进程崩溃、锁过期、别人误删这些问题。
正解SETNX 只是“占位”的第一步。安全地使用它需要四件事同时成立:命令原子(SET NX EX)、value 携带唯一标识、设置合理 TTL、配合看门狗续约。就像反光锥放好了,但锥上的定时贴可能烧完、别人可能把你的锥扔了。
为何常见:单机 Redis 操作“看起来瞬间完成”,容易让人忽略分布式环境里的故障模式。
误区主库挂了自动切到从库,锁一定还在,所以不需要 Redlock 那套复杂的多节点方案。
正解Redis 主从是异步复制。主库刚执行完 SETNX 还没复制到从库就宕机,从库升主后锁就丢了。Redlock 用多节点多数投票对冲的正是这种丢锁风险。
为何常见:对主从复制的异步性理解不足,误以为主从是“同步”的。
误区Redlock 有 5 个节点 + 多数同意,理论完备,生产环境用了就高枕无忧。
正解Redlock 在时钟回拨、进程停顿、网络分区组合拳下仍有安全窗口。它降低了单点故障概率,但没有消除所有风险。对极端一致性场景,还要叠加隔离令牌(fencing token)。
为何常见:“多数投票”听起来像 Paxos/Raft,让人误以为 Redlock 达到了同样的强一致级别。
误区害怕锁卡死,把过期时间设成 100ms,结果业务稍微慢一点锁就没了,另一个线程冲进来把数据写坏。
正解TTL 过短,业务未完成锁就被抢走;过长,故障恢复慢。正确做法是“业务耗时留 3-5 倍余量 + 看门狗续约”,而不是靠极端值走钢丝。
为何常见:只看到“锁过期能防死锁”,没看到“锁提前过期会破坏互斥”。
误区ZK 强一致、临时节点自动清理,所以任何场景都应该用 ZK 锁。
正解ZK 每次写都要走 ZAB 半数落盘,吞吐远低于 Redis,还要额外维护 ZK 集群。性能敏感场景 Redis 更合适;强一致低吞吐场景 ZK 更稳。各有适用边界。
为何常见:拿“一致性等级”这一个维度做选型,忽略了性能与运维成本。
面试题
Q1. 在 Redis 中,SETNX 命令的核心语义是?
- A.键不存在时设置并返回成功← 正确答案
- B.键存在时强制覆盖旧值
- C.无论键是否存在都设置
- D.设置后立即自动过期
答案:A。SETNX 即 SET if Not eXists,只有键不存在时才写入,这是分布式锁互斥性的基础。B 是覆盖语义,C 是普通 SET,D 是 EX 选项的职责,均不符。
Q2. 为什么 SETNX 与 EXPIRE 分两步执行有风险?
- A.两步之间进程崩溃留下死锁← 正确答案
- B.两步都会占用大量网络带宽
- C.Redis 会拒绝执行两步命令
- D.两步执行结果必然不一致
答案:A。先 SETNX 成功、还没执行 EXPIRE 时进程崩溃,锁没有过期时间,永远占着车位。这就像只放了反光锥却没贴定时贴。正确做法是用 SET key value NX EX 30 或 Lua 脚本保证原子性。
Q3. 分布式锁的三大基本要求中,不包括?
- A.互斥性保证同一时刻独占
- B.死锁防护防止锁永不释放
- C.可重入允许同一线程重入
- D.锁必须具备跨机房同步能力← 正确答案
答案:D。三大要求是互斥、防死锁、可重入。跨机房同步是部署层面的需求,不是分布式锁的基本属性。就像停车场管理员的核心职责是“只停一辆车、人走能清理、自己人方便”,而不是“分店必须开遍全国”。
Q4. Redlock 算法中,5 个节点至少需要几个同意?
- A.至少需要全部五个节点同意
- B.至少需要三个节点表示同意← 正确答案
- C.只需要两个节点表示同意
- D.只需要主节点同意即可通过
答案:B。法定多数 Q = ⌊5/2⌋ + 1 = 3。全部同意要求太高,降低可用性;2 个同意会在网络分区时出现两把锁同时成立。连锁停车场至少要多数分店记得“车位归你”才算数。
Q5. Redlock 最著名的理论争议来自?
- A.数据库复制延迟导致数据丢失
- B.时钟漂移和停顿破坏互斥性← 正确答案
- C.网络分区导致锁永远超时
- D.内存溢出导致节点全面崩溃
答案:B。Martin Kleppmann 指出 Redlock 依赖各节点时钟同步,时钟漂移、GC 停顿会使锁在“自以为持有”时其实已过期,破坏互斥性。这正是 Redlock 安全边界的核心争议。
Q6. ZooKeeper 实现分布式锁常用的节点类型是?
- A.持久节点且不附带任何序号
- B.临时顺序节点并比较序号大小← 正确答案
- C.持久顺序节点并比较序号大小
- D.容器节点并监听全部子节点
答案:B。临时节点保证会话断开自动删除(防死锁),顺序节点保证全局递增序号(公平排队),两者结合即 ZK 锁的经典组合。持久节点不会自动清理,不适合做锁。
Q7. 看门狗(Watchdog)在 Redis 锁中的作用是?
- A.在锁即将过期前自动续约保活← 正确答案
- B.监控其他线程的锁并强制清理
- C.将 Redis 的操作日志实时备份
- D.检测网络延迟并重连主库节点
答案:A。看门狗就是那条“盯着荧光贴、快烧完就重贴”的狗。Redisson 默认每 10 秒检查一次,在剩余 TTL 不足一半时将过期时间重置,保证业务耗时超过初始 TTL 时锁不提前释放。
Q8. 某业务平均执行 800ms,异常峰值 3000ms,应如何设置 TTL?
- A.TTL 设为 800ms 保证快速释放
- B.按峰值 3000ms 设置并配看门狗← 正确答案
- C.TTL 固定 5000ms 且不做续约
- D.不设置 TTL 由业务自行释放
答案:B。TTL 过短会在峰值时提前释放锁,破坏互斥;过长则崩溃后恢复慢。正确做法是按峰值留余量(如 3000ms),并配看门狗在长尾场景续约。不设 TTL 则崩溃后必然死锁。
Q9. 以下哪些情况会导致 Redis 分布式锁失效?
- A.主从切换后新主上锁记录丢失← 正确答案
- B.持有锁线程发生长时间 GC 停顿← 正确答案
- C.锁提前过期被其他线程抢占← 正确答案
- D.键空间命中率下降或缓存失效
答案:ABC。主从异步复制会丢锁,GC 停顿会让锁在持有时过期,TTL 过短会让锁被提前抢占——三者都会打破互斥。键空间命中率是缓存性能指标,与锁的失效无关。
Q10. 关于 Redlock 的安全边界,以下说法正确的有?
- A.进程 GC 停顿期间锁可能过期← 正确答案
- B.隔离令牌可阻止旧任务写数据← 正确答案
- C.Redlock 依赖时钟不被回拨← 正确答案
- D.Redlock 在异步网络下绝对安全
答案:ABC。GC 停顿会让持锁者失去对锁的感知,锁过期被抢后原进程继续写数据——此时隔离令牌(fencing token)用递增序号让下游拒绝旧写入;时钟漂移/回拨会破坏多数判定,说明 Redlock 并非绝对安全。异步网络下它仍有安全窗口,D 错误。