Redisson分布式锁

分布式锁是用于解决分布式系统中操作共享资源时的数据一致性问题。

Redisson是架设在Redis基础上的Java驻内存数据网格。适应场景:分布式应用,分布式缓存,分布式服务

分布式会话管理,分布式redis客户端。

 

设计分布式锁要注意的问题:

互斥:分布式系统中运行的多个节点,必须确保在同一个时刻只能有一个节点的一个线程获得锁,

这是最基本的一点。

死锁:分布式系统中,可能产生死锁的情况要相对复杂一些。分布式系统是处于复杂网络环境中的,

当一个节点获得锁,如果他在释放锁之前挂掉了,或者因为网络故障无法执行释放锁的命令,都会导致

其他节点无法申请到锁。因此分布锁有必要设置时效,确保在未来的一定时间内,无论锁的节点发生了什么问题

最终锁都能被释放掉。

性能:对于访问量大的共享资源,如果针对其获得锁时造成长时间的等待,导致大量的节点拥塞,是绝对不能接受的

此外,排队等待锁的节点如果不知道锁何时会被释放,则只能隔一段时间尝试获取一次锁,这样无法保证资源的高效利用,因此当锁释放时,要能够通知等待队列,使一个等待节点能够立刻获得锁。

重入:考虑到一些应用场景和资源的高效利用,锁要设计成可重入的,就像 JDK 中的 ReentrantLock 一样,同一个线程可以重复拿到同一个资源的锁。

redisson的基本实现代码:

RLock lock = redisson.getLock("foobar"); // 1.获得锁对象实例
lock.lock(); // 2.获取分布式锁
try {
    // do sth.
} finally {
    lock.unlock(); // 3.释放锁
}

 

redisson开源框架的实现原理:
(1)加锁机制:如果某个客户端要加锁,如果该客户端面对的是一个redis cluster集群,它首先会根据hash节点选择
一台机器,注意仅仅是选择一台机器,这点很关键。然后执行一段lua脚本,脚本如下:
Redisson分布式锁
复杂的业务逻辑,可以通过封装在lua脚本中发送给redis,保证这段复杂业务逻辑执行的原子性。

那么,这段lua脚本是什么意思呢?这里KEYS[1]代表的是你加锁的那个key,比如说:

RLock lock = redisson.getLock("myLock");

这里你自己设置了加锁的那个锁key就是“myLock”。ARGV[1]代表的就是锁key的默认生存时间,默认30秒。

ARGV[2]代表的是加锁的客户端的ID,类似于下面这样:8743c9c0-0795-4907-87fd-6c719a6b4586:1

给大家解释一下,第一段if判断语句,就是用“exists myLock”命令判断一下,如果你要加锁的那个锁key不存在的话

,你就进行加锁。如何加锁呢?很简单,用下面的命令

hset myLock 8743c9c0-0795-4907-87fd-6c719a6b4586:1 1,

通过这个命令设置一个hash数据结构,这行命令执行后,会出现一个类似下面的数据结构:

Redisson分布式锁

 

上述就代表“8743c9c0-0795-4907-87fd-6c719a6b4586:1”这个客户端对“myLock”这个锁key完成了加锁。

接着会执行“pexpire myLock 30000”命令,设置myLock这个锁key的生存时间是30秒

好了,到此为止,加锁完成了。

 

(2)锁互斥机制

这时如果客户端2来尝试加锁,执行lua脚本,第一个if判断会执行“exists myLock”,发现myLock这个锁key

已经存在了。接着第二个if判断,判断一下,myLock锁key的hash数据结构中,是否包含客户端2的ID,

但是明显不是的,因为那里包含的是客户端1的ID。

所以,客户端2会获取到pttl myLock返回的一个数字,这个数字代表了myLock这个锁key的剩余生存时间。

比如还剩15000毫秒的生存时间。此时客户端2会进入一个while循环,不停的尝试加锁。

 

(3)看门狗自动续期机制

客户端1加锁的锁key默认生存时间才30秒,如果超过了30秒,客户端1还想一直持有这把锁,怎么办呢?

只要客户端1一旦加锁成功,就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,

如果客户端1还持有锁key,那么就会不断的延长锁key的生存时间。

 

(4)可重入加锁机制

那如果客户端1都已经持有了这把锁了,结果可重入的加锁会怎么样呢?比如下面这种代码:

Redisson分布式锁

这时我们来分析一下上面那段lua脚本。第一个if判断肯定不成立,“exists myLock”会显示锁key已经存在了。

第二个if判断会成立,因为myLock的hash数据结构中包含的那个ID,就是客户端1的那个ID,

也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此时就会执行可重入加锁的逻辑,他会用:

incrby myLock 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1  ,通过这个命令,对客户端1的加锁次数,

累加1。此时myLock数据结构变为下面这样:

 

Redisson分布式锁

那个myLock的hash数据结构中的那个客户端ID,就对应着加锁的次数。

 

(5)释放锁机制

 

如果执行lock.unlock(),就可以释放分布式锁,此时的业务逻辑也是非常简单的。其实说白了,

就是每次都对myLock数据结构中的那个加锁次数减1。如果发现加锁次数是0了,说明这个客户端已经不再持有锁了,

此时就会用:“del myLock”命令,从redis里删除这个key。然后呢,另外的客户端2就可以尝试完成加锁了。

 

上一篇:Linux单网卡配置方法


下一篇:redisson中的看门狗机制总结