开课吧孤尽T31训练营学习笔记-DAY29-redisson分布式锁

149 阅读3分钟

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存储

  1. KEY: 锁的名字
  2. 字段:UUID+加锁线程id
  3. 值:重入次数

image.png

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);

image.png

二、 原理解析

2.1 加锁原理

image.png

image.png

这个代码一共三个分支,解决三个问题:

1. 加锁

还没有被加锁时,设置UUID:threadID = 1, 设置过期时间。

2. 锁重入

KEY和字段都存在,则是锁重入,执行命令incrby UUID:1 1

3. 锁互斥

判断有KEY,没有我的字段,则返回过期时间,客户端等待。

2.2 释放锁原理

image.png

image.png

释放锁步骤:

  1. 判断KEY是否存在;
  2. 如果不存在,则返回nil;
  3. 如果存在则使用hincrby -1, 减1
  4. 减去1后,如果counter>0,则返回0;否则删除key
  5. 用publish广播锁释放消息

订阅Channel源码如下:

image.png

2.3 自动续期原理

image.png

三、 分段锁

3.1 为什么需要分段锁

由于加锁必然会伤害系统的吞吐量。如果业务操作耗时较长,那么1秒钟处理的业务量很有限,失去了分布式系统的优势。

比如秒杀系统,假设扣减库存生成订单的操作耗时200ms,那么1秒钟只能处理5个业务。显然是不符合要求的。

3.2 分段锁设计

将原始业务分片,不如将原始库存10000分成10个1000,那么加锁时,可以有10个锁去获取,获取到对应的分段,则去扣减对应分段的库存即可。

这个redisson没有原始的支持,需要从业务本身的特点来做分段处理。

分段锁的设计,基于map reduce原理,