how to use redission
Redission
What
我们来写个例子来使用下redis的分布式锁,自己写的分布式锁和redission的区别:
|
看完上面写的代码,跟我们自己写是不是”差不多”的感觉。
但是看完上面的代码,有没有两个疑问🤔️。(这是redission帮我们做了)
- 问题1:分布式锁的过期时间去哪里?
- 问题2:value设置的值是什么?
官网参考:Redis分布式锁-Redlock,实现分布式锁需要同时满足下面三个条件
- 安全属性(Safety property): 独享(相互排斥)。在任意一个时刻,只有一个客户端持有锁。
- 活性A(Liveness property A): 无死锁。即便持有锁的客户端崩溃(crashed)或者网络被分裂(gets partitioned),锁仍然可以被获取。
- 活性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设置的值是什么
设置的值,很容易我们直接通过客户端软件,连接上去看下就知道了。
我们可以知道三个知识点。
- 我们设置的key是hash结构,而不是我们常规设置的string,这里又是一个知识点
- hash对应的key是一个UUID:1
- 有的在网上说只需要设置一个ok字符进去,这是不符合redis-redLock的设计理论的
- 但是这个1是什么含义呢?我们下面会跟大家揭晓
- 我们设置key是有过期时间的,解决了我们的第一个问题。是有设置过期时间的
接下来我们通过分析Redission的源码来分析下,框架是怎么做的。
org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean) |
我们debug一步步进去,最后一定会来到这个方法org.redisson.RedissonLock#tryLockInnerAsync
接下来我们好好分析下面的执行流程。