少女祈祷中...
NEKO
知识库

redis 分布式锁面试要点

一、基础概念类

Q1:什么是分布式锁?为什么需要?

  • 分布式环境下,多个进程/节点互斥访问共享资源。

  • 单机锁(synchronized、ReentrantLock)只在单 JVM 有效。

  • 分布式锁保证跨节点互斥。

  • 场景:库存扣减、订单防重、定时任务防重复执行。

Q2:分布式锁有哪些实现方式?

方案

特点

Redis

性能高,常用

ZooKeeper

强一致,临时顺序节点

数据库

唯一索引/行锁,简单但性能差

etcd

强一致,Raft

Q3:分布式锁要满足哪些条件?

  1. 互斥:同一时刻只有一个持有者。

  2. 防死锁:持有者挂了锁能释放,需过期时间。

  3. 防误删:只能删自己的锁。

  4. 可重入(可选):同一线程可重复获取。

  5. 高可用:锁服务不能单点。

二、SETNX 基础实现类

Q4:怎么用 Redis 实现分布式锁?

SET lock_key unique_value NX EX 30
  • NX:key 不存在才设置。

  • EX 30:30 秒过期,防死锁。

  • unique_value:唯一标识,防误删。

Q5:为什么用 SET NX EX 而不是 SETNX + EXPIRE?

  • SETNX + EXPIRE 两条命令非原子。

  • 如果 SETNX 后宕机,EXPIRE 没执行,锁永不过期,死锁。

  • SET NX EX 一条命令原子完成。

  • 这是必须用 SET 的原因。

Q6:value 为什么用唯一标识?

  • 防止误删别人的锁。

  • 场景:A 持锁,超时释放,B 拿到锁,A 执行完删锁,删了 B 的锁。

  • value 存 UUID + 线程 ID,删除前校验。

Q7:怎么正确删锁?

用 Lua 脚本保证原子性:

if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
  • 先校验 value 是否是自己,再删。

  • 校验和删除必须原子,否则仍有误删窗口。

Q8:为什么删锁要用 Lua?

  • GET + DEL 两条命令非原子。

  • 中间可能锁过期、别人拿到锁,导致误删。

  • Lua 在 Redis 中原子执行,避免窗口。

Q9:锁过期了业务没执行完怎么办?

  • 需要锁续期(看门狗)。

  • 后台线程定期检查,业务没完就延长 TTL。

  • Redisson 的看门狗默认每 10 秒续期到 30 秒。

Q10:锁的过期时间怎么设?

  • 要大于业务执行时间。

  • 太短:业务没完锁就释放,并发问题。

  • 太长:持有者挂了,其他等待久。

  • 配合续期机制更稳。

三、Redisson 类

Q11:Redisson 是什么?

  • Java 的 Redis 客户端,封装了分布式锁。

  • 提供 RLock,支持可重入、自动续期、阻塞等待。

  • 底层用 Lua 脚本保证原子性。

Q12:Redisson 的可重入怎么实现?

  • 用 Hash 结构存锁:key -> {uuid:threadId -> count}。

  • 同一线程再次加锁,count + 1。

  • 释放时 count - 1,减到 0 才删 key。

  • 用 Lua 保证原子。

Q13:Redisson 的看门狗是什么?

  • 加锁成功后,启动后台线程定时续期。

  • 默认 lockWatchdogTimeout=30s,每 10s 续到 30s。

  • 业务没完就不断续期。

  • 业务结束或宕机,不再续期,锁自动过期。

Q14:看门狗在什么情况下不生效?

  • 用 lock() 无参才启用看门狗。

  • 用 lock(10, TimeUnit.SECONDS) 指定时间,不启用看门狗。

  • 需手动处理续期或确保业务在 TTL 内完成。

Q15:Redisson 加锁流程?

  1. 用 Lua 脚本尝试 HSET 锁,设置 TTL。

  2. 成功则返回,启动看门狗。

  3. 失败则订阅锁释放消息,等待重试。

  4. 释放时减 count,为 0 删 key,发布消息唤醒等待者。

Q16:Redisson 怎么处理锁等待?

  • 用 Redis 的 Pub/Sub,锁释放时通知等待者。

  • 避免轮询,减少无效请求。

  • 等待超时可配。

四、Redlock 类

Q17:什么是 Redlock?

  • Redis 作者提出的多节点分布式锁算法。

  • 部署 N 个独立 Redis 主节点(通常 5 个)。

  • 客户端依次向所有节点加锁。

  • 多数派成功且总耗时小于锁有效期,才算加锁成功。

Q18:Redlock 的加锁流程?

  1. 记录开始时间。

  2. 依次向 N 个节点用相同 key/value 加锁,设超时。

  3. 如果多数派(N/2+1)成功,且总耗时 < 锁 TTL,则成功。

  4. 否则向所有节点解锁,重试。

Q19:Redlock 有什么争议?

  • Martin Kleppmann 质疑:

    • 依赖系统时钟,时钟漂移可能破坏互斥。

    • 没有 fencing token,GC 停顿可能导致旧持有者仍写。

  • Redis 作者 Antirez 反驳:

    • 时钟漂移可用漂移因子处理。

    • 不是所有场景都需要 fencing。

  • 结论:Redlock 有争议,强一致场景慎用。

Q20:什么是 fencing token?

  • 每次加锁生成递增的 token。

  • 写资源时带上 token,存储端拒绝旧 token。

  • 解决“锁过期但旧持有者仍在写”的问题。

  • Redlock 本身不提供,需自行实现。

Q21:Redlock 和单节点锁怎么选?

  • 单节点:简单,但 Redis 挂了锁不可用。

  • 主从 + 哨兵:故障转移可能丢锁。

  • Redlock:多节点,但复杂且有争议。

  • 一般业务用 Redisson 单节点/哨兵即可,强一致用 ZooKeeper/etcd。

Q22:主从架构下锁会丢吗?

  • 会。主库加锁后未同步,主库挂了,从库提升,锁丢失。

  • 其他客户端可再次加锁,互斥被破坏。

  • Redlock 或 WAIT 可缓解。

五、常见问题与坑类

Q23:锁超时导致并发问题怎么解决?

  • 看门狗续期。

  • 业务异步化,快速释放锁。

  • fencing token 兜底。

  • 评估业务执行时间,合理设 TTL。

Q24:Redis 主从切换锁丢失怎么办?

  • 用 Redlock。

  • 加锁后 WAIT 等从库确认。

  • 强一致用 ZooKeeper/etcd。

  • 业务层幂等兜底。

Q25:分布式锁能保证绝对互斥吗?

  • 不能绝对。

  • 网络分区、时钟漂移、GC 停顿都可能导致。

  • 要配合幂等、fencing token。

  • 锁是减少并发,不是绝对安全。

Q26:锁等待怎么实现?

  • 轮询:简单但浪费。

  • Pub/Sub:Redisson 用,锁释放通知。

  • 阻塞队列:本地等待。

Q27:公平锁怎么实现?

  • Redisson 支持 RedissonFairLock。

  • 用有序集合记录等待顺序。

  • 按顺序获取锁。

  • 性能低于非公平锁。

Q28:读写锁怎么实现?

  • Redisson RReadWriteLock。

  • 读锁共享,写锁互斥。

  • 用 Hash 记录读锁和写锁状态。

Q29:锁的 key 怎么设计?

  • 按资源命名,如 lock:order:123。

  • 加业务前缀,避免冲突。

  • 不要用固定 key,否则所有请求竞争一把锁。

Q30:锁的 value 怎么设计?

  • UUID:threadId,标识持有者。

  • 支持可重入和误删校验。

  • 不要用固定值。

六、实战与选型类

Q31:Redisson 和手写 SETNX 怎么选?

  • 生产用 Redisson:可重入、续期、等待、成熟。

  • 手写适合简单场景,但要注意原子性和误删。

  • 不要重复造轮子。

Q32:Redis 锁和 ZooKeeper 锁怎么选?

维度

Redis

ZooKeeper

性能

高

中

一致性

最终/弱

强

实现

简单

复杂

适用

高并发、容忍少量风险

强一致

Q33:库存扣减用分布式锁合适吗?

  • 可以,但性能差。

  • 更好用 Redis 原子操作(DECR)或 Lua。

  • 或数据库乐观锁。

  • 锁是兜底,不是首选。

Q34:分布式锁和幂等的关系?

  • 锁防并发,幂等防重复。

  • 锁可能失效,幂等兜底。

  • 重要操作两者都要。

Q35:怎么测试分布式锁?

  • 并发压测,验证互斥。

  • 模拟锁超时、主从切换。

  • 验证误删、死锁。

  • 监控锁等待和超时。

七、高频陷阱题

Q36:SETNX 成功后宕机,锁会死吗?

  • 如果只 SETNX 没 EXPIRE,会死锁。

  • 用 SET NX EX 原子设置,不会。

  • 所以必须用 SET。

Q37:锁过期后业务还在执行,会怎样?

  • 其他客户端可获取锁,并发执行。

  • 可能导致数据不一致。

  • 解决:续期、fencing token、幂等。

Q38:DEL 锁时没校验 value 会怎样?

  • 可能删别人的锁。

  • A 超时释放,B 拿锁,A 删了 B 的锁。

  • 必须 Lua 校验 value。

Q39:Redlock 能保证强一致吗?

  • 不能绝对。有时钟漂移、GC 问题。

  • 争议较大。

  • 强一致用 ZooKeeper/etcd。

Q40:看门狗一直续期会死锁吗?

  • 客户端宕机,看门狗停止,锁到期释放。

  • 不会死锁。

  • 但客户端假死(GC 停顿)可能续期,业务却没执行。

Q41:Redis 锁能重入吗?

  • 原生 SET NX 不能。

  • Redisson 用 Hash 实现可重入。

  • 手写要自己维护 count。

Q42:锁的 TTL 设为 -1 会怎样?

  • 永不过期,持有者挂了就死锁。

  • 必须设正数 TTL。

  • 配合续期。

评论