发布于 

how to use redission

Redission

What

我们来写个例子来使用下redis的分布式锁,自己写的分布式锁和redission的区别:

@Test
public void myConfigTest() {

Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");

RedissonClient redissonClient = Redisson.create(config);
RLock lock = redissonClient.getLock("noahPan");

lock.lock();
try {

TimeUnit.SECONDS.sleep(10);
System.out.println("redis task over");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}

}

看完上面写的代码,跟我们自己写是不是”差不多”的感觉。

但是看完上面的代码,有没有两个疑问🤔️。(这是redission帮我们做了)

  1. 问题1:分布式锁的过期时间去哪里?
  2. 问题2:value设置的值是什么?

官网参考:Redis分布式锁-Redlock,实现分布式锁需要同时满足下面三个条件

  1. 安全属性(Safety property): 独享(相互排斥)。在任意一个时刻,只有一个客户端持有锁。
  2. 活性A(Liveness property A): 无死锁。即便持有锁的客户端崩溃(crashed)或者网络被分裂(gets partitioned),锁仍然可以被获取。
  3. 活性B(Liveness property B): 容错。 只要大部分Redis节点都活着,客户端就可以获取和释放锁.

通过官网的资料我们可以知道:如果我们基于可靠的单机redis做分布式锁,只需要发送一条指令

SET key random_value NX PX 3000

接下来我们说说redis加锁和解放锁的细节

  • 加锁的命令必须时原子性的
  • 加锁设置的值,放的是random_value
  • value 的值设置为随机数主要是为了更安全的释放锁,释放锁的时候需要检查 key 是否存在,且 key 对应的值是否和我指定的值一样,是一样的才能释放锁。所以可以看到这里有获取、判断、删除三个操作,为了保障原子性,我们需要用 lua 脚本

为什么一定要Lua脚本来删除,这里我们展开下。

获取操作,只读不写,没有任何问题。但是我们现在是获取、判断、删除三步,如果不是原子性,会出现下面的问题:

  • 线程 A 在判断了 value 是自己放进去的,在执行 key 删除操作之前,程序 GC 导致了 STW。
  • STW 期间线程 A 的锁虽然没有执行删除操作,但是由于时间到期被 redis 释放了。
  • STW 之后,在线程 A 执行删除操作之前,线程 B 加了同样 key 的锁。
  • 结果你猜怎么着?线程 A 把线程 B 加的锁删除了。这就出问题了。

为什么Lua脚本可以解决这个问题呢?

因为lua脚本是原子性执行的,再加上redis是单线程执行命令的。在lua脚本执行完之前,其他命令都的等着

我们讨论下刚才上面说的第1个问题:redission加的锁没有设置过期时间的吗?

如果没有设置过期时间,会导致什么问题呢?其实很容易想到,假设我们没有设置过期时间。我们程序加锁完成之后,程序奔溃了,锁一直没有被释放。导致其他程序也获取不到锁,这就造成死锁了

Redission设置的值是什么

设置的值,很容易我们直接通过客户端软件,连接上去看下就知道了。

image-20220602152144167

我们可以知道三个知识点。

  1. 我们设置的key是hash结构,而不是我们常规设置的string,这里又是一个知识点
  2. hash对应的key是一个UUID:1
    • 有的在网上说只需要设置一个ok字符进去,这是不符合redis-redLock的设计理论的
    • 但是这个1是什么含义呢?我们下面会跟大家揭晓
  3. 我们设置key是有过期时间的,解决了我们的第一个问题。是有设置过期时间的

接下来我们通过分析Redission的源码来分析下,框架是怎么做的。

org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)

我们debug一步步进去,最后一定会来到这个方法org.redisson.RedissonLock#tryLockInnerAsync

image-20220602154513365

接下来我们好好分析下面的执行流程。