redisson分布式锁
在之前文章redis分布式锁 juejin.cn/post/703369… 中,分享了通过原生的redis实现分布式锁的一些方案,主要做法是保持redis操作的原子性。最后遗留的问题, 锁过期问题是没有很好解决的。
那么redisson就是解决这个问题的终极利器。redisson是基于Netty的redis客户端,不但能操作原生的redis数据结构,还为使用者提供了一系列具有分布式特性的常用工具类,实现了分布式锁。
一、redisson分布式锁
1.1 redisson分布式锁的根接口:RLock
public interface RLock extends Lock, RLockAsync {
boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
void lock(long leaseTime, TimeUnit unit);
boolean isLocked();
boolean isHeldByCurrentThread();
}
lock接口特点
如果没有拿到锁,就一直等着;
加锁永久加锁,需要自行释放。实现机制加锁30秒,采用watchdog每10秒自动延期。
tryLock接口特点
指定了等待时间和锁定时间,需要判断是否拿到锁;
根据返回值,即可判断是否拿到了锁;
判断拿到锁不能依赖于isLock接口来判断,锁被任意线程拿到,isLock都会返回true;判断加锁需要使用isHeldByCurrentThread来判断。
1.2 锁的存储结构
锁采用HASH存储
- KEY: 锁的名字
- 字段:UUID+加锁线程id
- 值:重入次数
1.3 支持锁重入
锁重入后,value值+1
isLock = disLock.tryLock(2000, 150000, TimeUnit.MILLISECONDS);
isLock = disLock.tryLock(2000, 150000, TimeUnit.MILLISECONDS);
isLock = disLock.tryLock(2000, 150000, TimeUnit.MILLISECONDS);
Thread.sleep(180000);
二、 原理解析
2.1 加锁原理
这个代码一共三个分支,解决三个问题:
1. 加锁
还没有被加锁时,设置UUID:threadID = 1, 设置过期时间。
2. 锁重入
KEY和字段都存在,则是锁重入,执行命令incrby UUID:1 1
3. 锁互斥
判断有KEY,没有我的字段,则返回过期时间,客户端等待。
2.2 释放锁原理
释放锁步骤:
- 判断KEY是否存在;
- 如果不存在,则返回nil;
- 如果存在则使用hincrby -1, 减1
- 减去1后,如果counter>0,则返回0;否则删除key
- 用publish广播锁释放消息
订阅Channel源码如下:
2.3 自动续期原理
三、 分段锁
3.1 为什么需要分段锁
由于加锁必然会伤害系统的吞吐量。如果业务操作耗时较长,那么1秒钟处理的业务量很有限,失去了分布式系统的优势。
比如秒杀系统,假设扣减库存生成订单的操作耗时200ms,那么1秒钟只能处理5个业务。显然是不符合要求的。
3.2 分段锁设计
将原始业务分片,不如将原始库存10000分成10个1000,那么加锁时,可以有10个锁去获取,获取到对应的分段,则去扣减对应分段的库存即可。
这个redisson没有原始的支持,需要从业务本身的特点来做分段处理。
分段锁的设计,基于map reduce原理,