游乐游手机版
首页/数据库/文章详情

Redis分布式锁8个常见面试题全面深度解读与总结

时间:2026-07-28 22:29
Redis分布式锁通过SETNXEX原子操作加锁、唯一value标识及Lua脚本安全解锁实现。需处理锁过期续期(看门狗)和主从切换丢锁问题,生产推荐Redisson框架。

1. 为什么需要分布式锁?

先从一个经典场景说起——双11秒杀,10000个人抢100个商品。

Redis分布式锁中8个常见面试题小结

单机锁为什么不行?因为秒杀系统通常有10台服务器,每台都有自己的内存,锁只能锁住自己,管不了别的机器。这时候,需要一个所有服务器都能访问的共享锁,Redis分布式锁就是为此而生的。它解决了多实例环境下的并发互斥问题,也是分布式系统面试中的高频考点。

2. 最简单的分布式锁怎么写?(错误示范)

// ❌ 新手最容易写的错误代码
public boolean lock(String key) {
    String result = jedis.setnx(key, "1");  // 尝试加锁
    return result == 1;  // 1表示加锁成功
}
public void unlock(String key) {
    jedis.del(key);  // 删除锁
}

这个写法问题很大。

  • 死锁风险:如果程序在加锁后崩溃了,锁永远都不会释放,导致其他线程无法获取锁。
  • 误删别人锁:A的锁超时释放了,B获得锁,结果A醒来之后把B的锁给删了,破坏了锁的安全性。

3. 怎么设置过期时间?(还是不对)

// ❌ 这个也有问题
public boolean lock(String key, int seconds) {
    // 两步操作:1.加锁 2.设置过期时间
    Long result = jedis.setnx(key, "1");
    if (result == 1) {
        jedis.expire(key, seconds);  // 设置过期
        return true;
    }
    return false;
}

问题在于,setnx和expire是两步操作,不是原子的。如果setnx成功,但expire之前程序崩溃了,锁就变成永久的了——这恰恰是刚才提到的死锁问题。在Redis分布式锁的实现中,原子性至关重要。

4. 正确写法是怎样的?

// ✅ 正确的加锁(一步完成)
public boolean lock(String key, String value, int seconds) {
    // 一条命令完成:加锁+设置过期时间
    String result = jedis.set(key, value, "NX", "EX", seconds);
    return "OK".equals(result);
}

// ✅ 安全的解锁
public void unlock(String key, String value) {
    // 用Lua脚本保证原子操作:检查值再删除
    String script = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "   return redis.call('del', KEYS[1]) " +
        "else " +
        "   return 0 " +
        "end";
        
    jedis.eval(script, 1, key, value);
}

核心要点有三个:

  1. 一条命令搞定:SET key value NX EX seconds,加锁和设置过期时间是原子操作,这是Redis分布式锁的核心原理。
  2. value用唯一标识:比如UUID或线程ID,而不是固定值“1”,这样能保证锁的持有者身份可识别。
  3. 原子删除:用Lua脚本先检查value是不是自己的,再删除,避免误删别人的锁,确保解锁安全。

5. 锁的value为什么不能用“1”?

// ❌ 错误:大家value都一样
jedis.set("lock", "1", "NX", "EX", 10);

// ✅ 正确:每人一个唯一标识
String myId = UUID.randomUUID().toString();
jedis.set("lock", myId, "NX", "EX", 10);

来看一个实际场景:

线程A:获得锁,value="A123",超时10秒

线程A:执行了15秒(锁在第10秒已过期)

线程B:获得锁,value="B456"

线程A:终于执行完,要删除锁 → 删了线程B的锁!❌

用唯一标识就能避免这种情况:删除前检查value是否还是自己的,不是就别动。这是Redis分布式锁面试题中常见的陷阱,也是实现可靠锁的关键细节。

6. 业务没执行完,锁过期了怎么办?

方案1:设置合理的过期时间。提前评估业务执行时间,把过期时间设得足够长。

// 评估业务时间,设置更长过期
jedis.set("lock", uuid, "NX", "EX", 30);  // 设置30秒

方案2:自动续期(看门狗模式)。启动一个线程定期检查,业务还没执行完就延长锁的过期时间。这是Redisson框架的默认机制,可以避免锁提前释放。

// 启动一个线程,定期续期
new Thread(() -> {
    while (业务没执行完) {
        Thread.sleep(8000);  // 8秒续一次
        
        // 如果是自己的锁,就延长过期时间
        jedis.expire("lock", 10);
    }
}).start();

7. Redis主从切换会丢锁吗?

会!这是Redis分布式锁最大的隐患,也是面试中常被追问的高阶问题。

场景还原:

1. 线程A在主节点获得锁

2. 主节点宕机(锁数据还没同步到从节点)

3. 从节点变成新主节点

4. 线程B在新主节点获得“相同”的锁

结果:A和B同时持有了锁!

解决方案有几个:

  1. 用RedLock算法:在多个Redis实例上加锁,提高可靠性,但实现复杂且性能较差。
  2. 用ZooKeeper:本质上更适合分布式锁场景,提供强一致性保证。
  3. 接受风险:在业务层面做幂等处理,让重复执行不影响最终结果,适用于对一致性要求不高的场景。

8. 实际开发用什么?(不要造轮子!)

// 使用Redisson框架(最省心)
RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("myLock");
try {
    lock.lock();  // 加锁(自动续期)
    // 执行业务...
} finally {
    lock.unlock();  // 解锁
}

面试对比表

方案优点缺点适用场景
Redis单节点简单、快主从切换丢锁测试环境、不重要的锁
RedLock相对可靠实现复杂、性能差重要的业务锁
Redisson功能全、自动续期依赖框架推荐的生产方案
ZooKeeper最可靠性能较差强一致性的场景

面试常见问题

Q1: “说一下Redis分布式锁的实现原理”

答:用SET命令的NX和EX参数,NX保证只有一个能设置成功,EX设置过期时间防止死锁。删除时用Lua脚本原子操作,避免删别人锁。这是Redis分布式锁最基础也是最经典的实现方式。

Q2: “Redis锁和ZooKeeper锁的区别?”

答:Redis是AP系统,性能好但可能丢锁;ZooKeeper是CP系统,可靠但性能差。选择取决于需求:要高性能用Redis,要可靠性用ZooKeeper。在分布式系统设计中,CAP理论决定了不同组件的适用边界。

Q3: “怎么实现可重入锁?”

答:在value里记录线程ID和重入次数。加锁时如果是同一线程,计数+1;解锁时计数-1,计数为0才真正删除锁。这样可以避免同一个线程重复加锁导致的死锁问题。

记住三句话

  1. 加锁要原子:SET key uuid NX EX seconds
  2. 解锁要安全:用Lua脚本先检查再删除
  3. 生产别造轮子:直接用Redisson

快速检查清单

你的分布式锁:

  • 设置过期时间了吗?
  • value是唯一的吗?
  • 删除前检查value了吗?
  • 考虑锁续期了吗?
  • 知道主从切换会丢锁吗?

满足这5条,面试官就难不倒你了。

来源:https://www.jb51.net/database/3599084z7.htm
上一篇Redis Key命名规范实战指南 下一篇Redis 8.2.1 密码修改密文操作方案
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis是什么:核心特性、架构与应用场景解析
数据库 · 2026-09-01

Redis是什么:核心特性、架构与应用场景解析

Redis是一款基于内存的键值型NoSQL数据库,以超高读写速度和丰富的数据结构著称。本文系统梳理Redis的核心特性、架构组成、性能优势及典型应用场景,并通过与Memcached、MySQL、MongoDB的对比,帮助开发者快速判断Redis是否适合当前业务需求。

Windows 安装 MongoDB 完整图文教程
数据库 · 2026-09-01

Windows 安装 MongoDB 完整图文教程

本文详细介绍在 Windows 系统上安装 MongoDB 的完整流程。从官网下载 MSI 安装包开始,逐步演示自定义安装路径、配置 Windows 服务、跳过 MongoDB Compass 等关键选项,并提供通过系统服务列表验证安装是否成功的方法,帮助开发者快速搭建本地 MongoDB 环境。

Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动
数据库 · 2026-09-01

Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动

本文详解在 Linux 系统下安装 MongoDB 的完整流程,涵盖依赖包安装、二进制包下载解压、环境变量配置、数据与日志目录创建及服务启动验证。通过标准化命令与路径说明,帮助开发者快速完成部署并确认服务状态。

MacOS安装MongoDB完整教程
数据库 · 2026-09-01

MacOS安装MongoDB完整教程

本文介绍在MacOS系统下安装MongoDB的完整流程,涵盖下载、解压、目录配置、环境变量设置及服务启动。通过明确的命令与参数说明,帮助开发者快速完成环境搭建并验证安装结果。

Ubuntu系统安装与配置Redis完整指南
数据库 · 2026-09-01

Ubuntu系统安装与配置Redis完整指南

本文详解在Ubuntu系统中安装Redis的两种主流方式:apt在线安装与源码编译安装。涵盖版本选择逻辑、服务启停与状态检查、连接验证方法,以及在线练习工具与桌面GUI客户端的对比与使用建议,帮助开发者快速搭建并验证Redis运行环境。