真实面经题目 · 原创解析
Redis Lua 脚本怎么实现分布式锁
这题考察 Redis 分布式锁的正确最小模型:SET NX PX 原子加锁、唯一 value 标识持有者、Lua 原子校验后删除,以及过期、续期和主从故障风险。
真实面经题目 · 原创解析
这题考察 Redis 分布式锁的正确最小模型:SET NX PX 原子加锁、唯一 value 标识持有者、Lua 原子校验后删除,以及过期、续期和主从故障风险。
Redis 分布式锁的基础做法是加锁时执行 SET lockKey uniqueValue NX PX expireMs,NX 保证 key 不存在才成功,PX 设置过期时间避免持锁进程崩溃后死锁。uniqueValue 必须能标识锁持有者,例如请求 ID 或进程加线程唯一值。释放锁时不能直接 DEL,因为自己的锁可能已经过期并被别人重新获得;应该用 Lua 在 Redis 内原子执行“先比较 value 是否等于自己的 uniqueValue,再删除”。Lua 的价值不是单纯减少网络请求,而是让 GET 和 DEL 之间没有竞态。进一步还要说明锁超时、续期、可重入、主从切换和 Redlock 的适用边界。
SET key value NX PX ttl 把不存在才写入和设置过期时间合在一次执行里。老式 SETNX 后再 EXPIRE 中间如果进程崩溃,可能留下没有过期时间的死锁。
value 不能随便写固定字符串。它要唯一标识本次加锁请求,释放锁、续期和排查问题都要用它判断当前客户端是否仍是锁持有者。
直接 DEL 的风险是客户端 A 执行业务太久,锁过期后客户端 B 获得锁,随后 A 执行 DEL 把 B 的锁删掉。正确做法是 value 相等才删除。
GET 和 DEL 如果分两次发,中间可能穿插其他客户端操作。Lua 脚本在 Redis 单线程执行模型下连续完成比较和删除,避免检查后状态变化。
TTL 太短会导致业务未完成锁先释放,TTL 太长会拖慢故障恢复。长任务需要 owner 校验下的续期机制,并在业务结束或失败时停止续期。
单 Redis 主从异步复制下,主节点写锁后未同步就故障,新的主节点可能没有锁记录。高可靠场景要评估 Redlock、fencing token、数据库约束或业务状态机兜底。
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
end
return 0 这两个命令之间如果客户端崩溃,锁 key 可能没有过期时间,造成死锁。SET NX PX 把加锁和设置 TTL 合成一条原子命令。
要覆盖业务正常耗时和抖动,并尽量短到故障后能快速恢复。不能准确估计时,需要续期机制、业务幂等和超时保护一起兜底。
可以做看门狗续期,但每次续期都要校验 owner,业务结束后停止续期。更关键的是业务写入要有 fencing token 或状态校验,防止旧持有者继续写。
Redis 复制通常是异步的。主节点加锁后还没同步就宕机,提升的新主可能没有锁记录,其他客户端会再次获得锁,导致并发持锁。