5、分布式锁-redission
5.1 分布式锁-redission功能介绍
基于setnx实现的分布式锁存在下面的问题:
重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如HashTable这样的代码中,他的方法都是使用synchronized修饰的,假如他在一个方法内,调用另一个方法,那么此时如果是不可重入的,不就死锁了吗?所以可重入锁他的主要意义是防止死锁,我们的synchronized和Lock锁都是可重入的。
不可重试:是指目前的分布式只能尝试一次,我们认为合理的情况是:当线程在获得锁失败后,他应该能再次尝试获得锁。
超时释放:我们在加锁时增加了过期时间,这样的我们可以防止死锁,但是如果卡顿的时间超长,虽然我们采用了lua表达式防止删锁的时候,误删别人的锁,但是毕竟没有锁住,有安全隐患
主从一致性: 如果Redis提供了主从集群,当我们向集群写数据时,主机需要异步的将数据同步给从机,而万一在同步过去之前,主机宕机了,就会出现死锁问题。
那么什么是Redission呢
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。
Redission提供了分布式锁的多种多样的功能
🧠 理论理解:
-
在分布式系统中,我们必须保证多节点下对共享资源(如秒杀库存)的并发访问是安全的。
-
单用 setnx + 过期时间的 Redis 锁方案,虽然能解决基本的分布式锁问题,但存在重入性、自动续期、异常宕机、主从一致性等不足。
-
Redisson 是基于 Redis 实现的分布式对象工具库,它封装了分布式锁、集合、队列、信号量、读写锁等丰富的分布式同步组件,并用 Java 提供了高度抽象、可用性极强的 API。
-
重要特性包括:
-
可重入(防止重复加锁时死锁)
-
自动续期(看门狗机制,避免业务阻塞导致锁意外释放)
-
多节点一致性(使用 MultiLock、RedLock 等方式确保主从切换后的安全性)
-
高可用、高性能(完全利用 Redis 高速特性)
-
🏢 大厂实战理解:
-
在阿里、字节这类高并发场景下,电商秒杀、优惠券发放、订单去重等业务几乎都需要稳定的分布式锁体系。阿里内部很多系统直接用 Redisson 作为分布式锁工具,而非自研方案,主要原因是 Redisson 已经在开源界经受了千万级用户的稳定验证。
-
Google 和 OpenAI 的分布式训练任务调度、跨 GPU 节点的任务锁,也会采用类似 Redisson 或 etcd、Zookeeper 的分布式锁工具,确保全局唯一性,避免重复计算。
-
NVIDIA 在多机多卡的 AI 推理服务中,会用到主节点调度锁,防止多台服务器同时对同一批模型资源加载造成浪费。
1️⃣ 什么是分布式锁?相比单机锁,它解决了什么问题?
✅ 答:
分布式锁是一种跨进程、跨节点、跨 JVM 的全局互斥机制。相比单机锁(如 synchronized、ReentrantLock),分布式锁解决了在多节点环境中,多个服务实例需要共享资源(例如:分布式订单去重、全局任务调度、库存扣减)的并发竞争问题。它的核心目标是:
-
保证互斥性(同一时刻只能有一个节点获得锁)
-
保证可见性(所有节点对锁的状态一致感知)
-
保证高可用性(节点宕机锁不会失效或死锁)
大厂中分布式锁主要用于:限流、幂等、分布式事务、任务调度、库存扣减等高并发场景。
🌟 场景题 1:秒杀系统超卖问题
👉 你在负责设计一个大型电商的秒杀系统,需要确保:
-
单个用户同一商品只能下一单
-
秒杀库存不能被超卖
-
系统是分布式部署,后端有多节点、主从 Redis 集群
问:你如何设计分布式锁方案?为什么这样选?
✅ 答:
选择方案 → 使用 Redisson 分布式锁 + 单用户限流 + 一人一单 + 乐观扣减库存。
具体实现:
-
获取分布式锁,锁 key 为 lock:order:userId
-
使用 tryLock 重试获取锁,避免瞬时失败
-
持锁后检查是否已下单(查询数据库或缓存)
-
使用乐观锁扣减库存(SQL 加 version 或 stock > 0)
-
持锁期间处理完后释放锁
实现细节:
-
使用 Redisson WatchDog 机制,避免长时间业务锁过期
-
在多 Redis 节点环境中,使用 MultiLock 保证主从一致性
-
对于高并发流量,加限流或用消息队列削峰
可能遇到的问题:
-
锁重试频率过高导致 Redis 压力暴增 → 需要限频重试或加退避策略
-
单机内多线程可重入问题 → 必须用 Redisson 可重入锁
-
用户操作失败后锁未释放 → 确保 finally 中释放锁
5.2 分布式锁-Redission快速入门
引入依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
配置Redisson客户端:
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient(){
// 配置
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.150.101:6379")
.setPassword("123321");
// 创建RedissonClient对象
return Redisson.create(config);
}
}
如何使用Redission的分布式锁
@Resource
private RedissionClient redissonClient;
@Test
void testRedisson() throws Exception{
//获取锁(可重入),指定锁的名称
RLock lock = redissonClient.getLock("anyLock");
//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位
boolean isLock = lock.tryLock(1,10,TimeUnit.SECONDS);
//判断获取锁成功
if(isLock){
try{
System.out.println("执行业务");
}finally{
//释放锁
lock.unlock();
}
}
}
在 VoucherOrderServiceImpl
注入RedissonClient
@Resource
private RedissonClient redissonClient;
@Override
public Result seckillVoucher(Long voucherId) {
// 1.查询优惠券
SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
// 2.判断秒杀是否开始
if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
// 尚未开始
return Result.fail("秒杀尚未开始!");
}
// 3.判断秒杀是否已经结束
if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
// 尚未开始
return Result.fail("秒杀已经结束!");
}
// 4.判断库存是否充足
if (voucher.getStock() < 1) {
// 库存不足
return Result.fail("库存不足!");
}
Long userId = UserHolder.getUser().getId();
//创建锁对象 这个代码不用了,因为我们现在要使用分布式锁
//SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
RLock lock = redissonClient.getLock("lock:order:" + userId);
//获取锁对象
boolean isLock = lock.tryLock();
//加锁失败
if (!isLock) {
return Result.fail("不允许重复下单");
}
try {
//获取代理对象(事务)
IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
return proxy.createVoucherOrder(voucherId);
} finally {
//释放锁
lock.unlock();
}
}
🧠 理论理解:
-
Redisson 的使用非常简单:
-
引入 Maven 依赖;
-
配置 Redis 地址、密码、连接池等;
-
调用
getLock
获取分布式锁对象,tryLock()
尝试加锁,unlock()
释放锁。
-
-
它隐藏了复杂的底层细节(如 Lua 脚本、锁粒度、过期时间管理、锁自动续期等),让开发者专注于业务逻辑。
🏢 大厂实战理解:
-
在字节跳动的推荐系统中,推荐流任务会用 Redisson 锁做“同用户同时间段只推一次流”。
-
在阿里分布式账务系统中,多个微服务抢占账单任务时也会用 Redisson 锁防止重复计算或任务冲突。
-
在 Google 的 Cloud Tasks 系统中,队列任务可能通过分布式锁实现分布式协调,Redisson 类似的 Java 实现也很适合中小公司采用。
2️⃣ 你了解 Redisson 实现分布式锁的核心原理吗?
✅ 答:
Redisson 依赖 Redis 实现分布式锁,主要使用:
-
set nx ex 命令:保证互斥性和设置超时时间(避免死锁)
-
Lua 脚本:确保比对和删除锁的原子性,防止误删别人的锁
-
WatchDog 看门狗机制:自动续期,避免业务执行时间过长导致锁失效
-
可重入机制:利用 Hash(大 key + 小 key + 线程 id)记录锁重入次数
-
MultiLock:多 Redis 节点一致性锁,解决主从漂移一致性问题
这些机制共同构成了 Redisson 高可用、高可靠、高性能的分布式锁体系。
🌟 场景题 2:分布式任务调度系统
👉 你需要设计一个跨多节点的任务调度系统,例如每天凌晨分布式定时任务执行(如数据归档、日志汇总),保证同一时间只有一个节点执行。
问:你会用什么锁方案?怎么实现?
✅ 答:
选择方案 → 使用 Redisson 分布式锁(带 WatchDog)在任务节点中加互斥。
具体实现:
-
每个节点调度器都尝试获取锁(lock:cron:task:archive)
-
使用 lock(),由 WatchDog 自动续期,确保长任务期间锁不会失效
-
获得锁的节点执行任务,其他节点不执行
-
任务结束后手动 unlock
-
锁失效时间设置为任务预计最长时间的 1.5 倍
可能遇到的问题:
-
某个节点宕机导致锁挂住 → WatchDog 能自动释放
-
网络分区导致脑裂,多个节点同时认为自己持有锁 → 可以使用 MultiLock 防止主从一致性问题
-
执行任务失败未释放锁 → 必须严格在 finally 块里释放
5.3 分布式锁-redission可重入锁原理
在Lock锁中,他是借助于底层的一个voaltile的一个state变量来记录重入的状态的,比如当前没有人持有这把锁,那么state=0,假如有人持有这把锁,那么state=1,如果持有这把锁的人再次持有这把锁,那么state就会+1 ,如果是对于synchronized而言,他在c语言代码中会有一个count,原理和state类似,也是重入一次就加一,释放一次就-1 ,直到减少成0 时,表示当前这把锁没有被人持有。
在redission中,我们的也支持支持可重入锁
在分布式锁中,他采用hash结构用来存储锁,其中大key表示表示这把锁是否存在,用小key表示当前这把锁被哪个线程持有,所以接下来我们一起分析一下当前的这个lua表达式
这个地方一共有3个参数
KEYS[1] : 锁名称
ARGV[1]: 锁失效时间
ARGV[2]: id + ":" + threadId; 锁的小key
exists: 判断数据是否存在 name:是lock是否存在,如果==0,就表示当前这把锁不存在
redis.call('hset', KEYS[1], ARGV[2], 1);此时他就开始往redis里边去写数据 ,写成一个hash结构
Lock{
id + ":" + threadId : 1
}
如果当前这把锁存在,则第一个条件不满足,再判断
redis.call('hexists', KEYS[1], ARGV[2]) == 1
此时需要通过大key+小key判断当前这把锁是否是属于自己的,如果是自己的,则进行
redis.call('hincrby', KEYS[1], ARGV[2], 1)
将当前这个锁的value进行+1 ,redis.call('pexpire', KEYS[1], ARGV[1]); 然后再对其设置过期时间,如果以上两个条件都不满足,则表示当前这把锁抢锁失败,最后返回pttl,即为当前这把锁的失效时间
如果小伙帮们看了前边的源码, 你会发现他会去判断当前这个方法的返回值是否为null,如果是null,则对应则前两个if对应的条件,退出抢锁逻辑,如果返回的不是null,即走了第三个分支,在源码处会进行while(true)的自旋抢锁。
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);"
🧠 理论理解:
-
可重入锁:持有锁的线程可以再次获得该锁,并记录重入次数,释放时需要重复 unlock 同样次数。
-
Redisson 的可重入实现:使用 Redis Hash 结构,Key 为锁名,field 为
uuid:threadId
,value 为重入次数。 -
优势:解决单节点死锁问题,适配复杂业务调用链。
🏢 大厂实战理解:
-
在阿里、字节这类微服务架构中,一个 API 可能间接调用多个子服务,所有子服务都需要同一个锁,这就要求锁必须可重入。
-
在 Google Cloud 内部的 BigQuery 多租户环境中,查询调度锁也必须保证可重入,防止多次调度时出现死锁。
-
NVIDIA 多 GPU 环境下,模型加载锁必须保证同一任务在递归调用时不死锁。
3️⃣ Redisson 可重入锁是如何实现的?
✅ 答:
Redisson 在 Redis 中用 Hash 结构实现可重入锁:
-
大 key:锁的全局唯一标识(如 lock:order:123)
-
小 key:线程唯一标识(UUID:threadId)
-
value:记录重入次数
当同一个线程多次加锁时,只会在 Redis 中 +1 重入计数;释放锁时会逐次减 1,直到为 0 时才真正删除锁。
这种设计解决了:同一线程在递归或嵌套调用时的死锁风险,是与 synchronized、ReentrantLock 一致的设计。
🌟 场景题 3:订单号全局唯一 ID 生成
👉 你需要实现一个高并发订单号生成器,要求:
-
分布式部署,多个节点生成的 ID 不冲突
-
支持每秒生成上百万个 ID
-
要求高可用、容错
问:你会用什么方案?Redisson 能帮上什么?
✅ 答:
选择方案 → 使用 Redis + Lua 脚本或 Redisson 提供的原子计数器(RAtomicLong)。
具体实现:
-
使用 Redisson 的 RAtomicLong 对象,调用 incrementAndGet()
-
Redis 保证单点的高性能自增
-
如果多数据中心或多节点,使用 Redisson MultiLock 或分段 ID(节点号 + 本地递增)方案
-
Redis 宕机时用本地缓存或数据库兜底补偿
可能遇到的问题:
-
高并发冲突 → Redis 单点性能需调优,可用分布式雪花算法(如 Snowflake)结合
-
Redis 宕机不可用 → 用双写备份 Redis,结合数据库补偿方案
-
跨集群全局 ID 不一致 → 需要用 MultiLock 或中心化 ID 服务
5.4 分布式锁-redission锁重试和WatchDog机制
说明:由于课程中已经说明了有关tryLock的源码解析以及其看门狗原理,所以笔者在这里给大家分析lock()方法的源码解析,希望大家在学习过程中,能够掌握更多的知识
抢锁过程中,获得当前线程,通过tryAcquire进行抢锁,该抢锁逻辑和之前逻辑相同
1、先判断当前这把锁是否存在,如果不存在,插入一把锁,返回null
2、判断当前这把锁是否是属于当前线程,如果是,则返回null
所以如果返回是null,则代表着当前这哥们已经抢锁完毕,或者可重入完毕,但是如果以上两个条件都不满足,则进入到第三个条件,返回的是锁的失效时间,同学们可以自行往下翻一点点,你能发现有个while( true) 再次进行tryAcquire进行抢锁
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return;
}
接下来会有一个条件分支,因为lock方法有重载方法,一个是带参数,一个是不带参数,如果带带参数传入的值是-1,如果传入参数,则leaseTime是他本身,所以如果传入了参数,此时leaseTime != -1 则会进去抢锁,抢锁的逻辑就是之前说的那三个逻辑
if (leaseTime != -1) {
return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
如果是没有传入时间,则此时也会进行抢锁, 而且抢锁时间是默认看门狗时间 commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout()
ttlRemainingFuture.onComplete((ttlRemaining, e) 这句话相当于对以上抢锁进行了监听,也就是说当上边抢锁完毕后,此方法会被调用,具体调用的逻辑就是去后台开启一个线程,进行续约逻辑,也就是看门狗线程
RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(waitTime,
commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
if (e != null) {
return;
}
// lock acquired
if (ttlRemaining == null) {
scheduleExpirationRenewal(threadId);
}
});
return ttlRemainingFuture;
此逻辑就是续约逻辑,注意看commandExecutor.getConnectionManager().newTimeout() 此方法
Method( new TimerTask() {},参数2 ,参数3 )
指的是:通过参数2,参数3 去描述什么时候去做参数1的事情,现在的情况是:10s之后去做参数一的事情
因为锁的失效时间是30s,当10s之后,此时这个timeTask 就触发了,他就去进行续约,把当前这把锁续约成30s,如果操作成功,那么此时就会递归调用自己,再重新设置一个timeTask(),于是再过10s后又再设置一个timerTask,完成不停的续约
那么大家可以想一想,假设我们的线程出现了宕机他还会续约吗?当然不会,因为没有人再去调用renewExpiration这个方法,所以等到时间之后自然就释放了。
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getName() + " expiration", e);
return;
}
if (res) {
// reschedule itself
renewExpiration();
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
🧠 理论理解:
-
WatchDog 机制(看门狗):当业务执行超过锁的初始过期时间时,Redisson 自动为锁续期,避免任务未完成锁却失效。
-
Redisson 会默认开启定时任务,每隔 10 秒检查一次锁持有状态,如果还持有就延长 30 秒。
-
优势:避免人为指定过长的 leaseTime(导致长时间占用锁资源),也避免太短时间引发锁丢失。
🏢 大厂实战理解:
-
在字节的内容分发网络(CDN)中,缓存刷新任务通常较重,可能需要几十秒甚至几分钟,利用 WatchDog 机制能避免锁误释放。
-
在 Google DeepMind、OpenAI 等分布式 AI 推理中,某些任务占用资源时间极长,自动续期机制是保证分布式锁安全的关键。
-
在阿里云分布式数据库的迁移任务中,自动续期避免了迁移过程因超时被中断。
4️⃣ Redisson WatchDog 看门狗机制有什么作用?为什么重要?
✅ 答:
WatchDog 是 Redisson 的自动续期机制。
当业务持有锁超过初始 leaseTime,WatchDog 会定时(默认 10 秒)检查并续期(默认 30 秒),确保锁不会因为长时间业务阻塞而被误释放。
它的重要性在于:
-
避免业务处理超时后锁失效,导致数据不一致
-
免去人工设置过长的过期时间(提升系统吞吐)
-
保证高可靠性:线程宕机时 WatchDog 不会续期,锁能自动释放
5️⃣ 什么是 Redisson 的 MultiLock?它解决了什么问题?
✅ 答:
MultiLock 是 Redisson 实现的跨 Redis 节点多实例锁。它的原理是:
-
同时在多个 Redis 节点上加锁(如 3 个节点)
-
只要半数以上节点加锁成功,就认为锁成功
-
解锁时需要在所有节点上释放
它解决了: -
单节点 Redis 主从不一致的问题(例如主库写成功、未同步到从库时主库挂掉)
-
Redis 集群场景下的单点锁失效问题
是大厂在跨机房、跨集群部署下常用的高可用分布式锁方案。
🌟 场景题 4:商品库存系统,避免库存超卖和死锁
👉 你需要实现一个商品库存管理系统,要求:
-
多个服务节点可以同时操作库存(加库存、减库存)
-
保证库存不被超扣
-
系统高可用、低延迟
问:你设计的锁方案是什么?怎么保证安全与性能?
✅ 答:
选择方案 → 使用 Redisson 分布式读写锁(RReadWriteLock)。
具体实现:
-
读库存操作用读锁,可多线程并发读
-
写库存(加库存、减库存)用写锁,写期间阻塞读写
-
锁 key 为 lock:stock:productId
-
使用 tryLock 设置最大等待时间,避免锁饥饿
-
对大流量商品,用分片锁或热数据拆分优化
可能遇到的问题:
-
热点商品库存读写冲突严重 → 可用缓存或库存预扣设计优化
-
分布式锁粒度太粗导致全局串行 → 分产品或分库分片锁
-
Redis 集群主从不一致 → 用 MultiLock 保证可靠性
5.5 分布式锁-redission锁的MutiLock原理
为了提高redis的可用性,我们会搭建集群或者主从,现在以主从为例
此时我们去写命令,写在主机上, 主机会将数据同步给从机,但是假设在主机还没有来得及把数据写入到从机去的时候,此时主机宕机,哨兵会发现主机宕机,并且选举一个slave变成master,而此时新的master中实际上并没有锁信息,此时锁信息就已经丢掉了。
为了解决这个问题,redission提出来了MutiLock锁,使用这把锁咱们就不使用主从了,每个节点的地位都是一样的, 这把锁加锁的逻辑需要写入到每一个主丛节点上,只有所有的服务器都写入成功,此时才是加锁成功,假设现在某个节点挂了,那么他去获得锁的时候,只要有一个节点拿不到,都不能算是加锁成功,就保证了加锁的可靠性。
那么MutiLock 加锁原理是什么呢?笔者画了一幅图来说明
当我们去设置了多个锁时,redission会将多个锁添加到一个集合中,然后用while循环去不停去尝试拿锁,但是会有一个总共的加锁时间,这个时间是用需要加锁的个数 * 1500ms ,假设有3个锁,那么时间就是4500ms,假设在这4500ms内,所有的锁都加锁成功, 那么此时才算是加锁成功,如果在4500ms有线程加锁失败,则会再次去进行重试.
🧠 理论理解:
-
MultiLock 是 Redisson 提供的一种多节点一致性加锁方案。
-
它同时在多个独立的 Redis 节点上加锁,只有半数以上节点加锁成功才算成功,确保主从切换、单点故障下的锁安全。
-
优势:解决了单点 Redis 因主从不一致导致的锁丢失、死锁问题。
🏢 大厂实战理解:
-
在阿里、字节的多机房多集群部署中,分布式锁必须跨多 Redis 实例,否则主从漂移会带来重大业务风险。
-
在 Google Cloud Spanner 分布式数据库中,多节点锁保证事务一致性,类似原理被 Redisson MultiLock 借鉴。
-
OpenAI 在多数据中心推理任务调度中,也需要这种跨实例锁保证任务唯一性,避免重复算力浪费。
6️⃣ 你了解 Redisson 分布式锁的主从一致性问题吗?它是如何规避的?
✅ 答:
主从一致性问题是指:
-
Redis 主节点接收写入锁命令,但尚未同步到从节点时主节点宕机
-
哨兵或集群将从节点切换为主节点,但从节点中未记录锁信息
-
导致锁丢失、出现并发竞争、数据不一致
Redisson 的解决方案: -
使用 MultiLock / RedLock(基于多主多副本)机制
-
使用半数以上节点加锁成功才算成功
-
提高系统的锁可靠性,即使部分节点失效,整体锁依然有效
这种设计借鉴了分布式系统中的 Paxos、Raft 一致性思想。
7️⃣ 在实际项目中,如何选择分布式锁的实现方式?
✅ 答:
选择要看业务需求:
-
如果只是单机多线程:用 Java 的 synchronized、ReentrantLock
-
如果是简单单节点 Redis:用 set nx ex + Lua 脚本(自研简单锁)
-
如果是高可用、主从或集群场景:用 Redisson
-
如果有强一致性需求:用 Redisson MultiLock、Zookeeper、etcd
-
如果是数据库强一致:可以直接用数据库悲观锁(如 for update)
大厂实践中,通常优先选择现成的中间件(如 Redisson)而不是自研,因为它已经解决了各种边界问题和坑。
8️⃣ Redisson 分布式锁的使用有哪些坑?
✅ 答:
-
锁粒度过大:导致系统串行化,降低吞吐
-
锁粒度过小:分布式协调成本高
-
锁未释放:业务异常或超时未解锁,导致死锁
-
锁误删:多个线程误删他人锁(需用 Lua 脚本保证原子性)
-
主从不一致:单节点锁在主从切换时丢失(需用 MultiLock)
-
高并发场景下:锁竞争激烈,业务需要引入限流、降级、幂等等配合方案
大厂在设计分布式锁方案时,通常还会用监控和报警系统实时追踪锁状态。
🌟 场景题 5:大规模活动系统秒杀+限流+降级
👉 你设计一个双 11 活动系统,需要保障:
-
秒杀场景下,避免库存超卖
-
遇到 Redis 或数据库故障,系统可自动降级
-
高并发流量下,Redis 和后端服务不能被打挂
问:分布式锁设计和整体防护方案?
✅ 答:
选择方案 → Redisson 分布式锁 + 异步化处理 + 熔断降级。
具体实现:
-
前端请求到达后端,先用令牌桶或漏斗限流
-
后端进入后,使用 Redisson 分布式锁保证库存和订单操作安全
-
核心库存扣减、订单落库操作异步化(MQ 削峰)
-
当 Redis、数据库等不可用时,快速熔断,直接返回降级响应或进入备用方案(如预缓存、静态页)
-
全链路监控锁状态、系统健康,自动报警
可能遇到的问题:
-
秒杀时锁竞争剧烈导致 Redis 压力暴增 → 需要多层缓存和限流削峰
-
分布式锁占用时间过长 → WatchDog 续期或优化业务逻辑
-
锁异常或宕机 → 异步重试、补偿机制