I asked Redis lock in the interview, my face is green...

In the Nth week of working at home, I don’t know if the keyboard and monitor on the author’s workstation missed me. I don’t know if the dust was too serious and was thrown away by the cleaning lady.


image.png

Image courtesy of pexels


The author brings an article about Redis locks today, and even knocks and draws the code to write this article. There are some details. Those who are not clear about Redis locks may wish to take a look.


If it is an experienced pot friend who is picking up faults, then the author is even more grateful! There is not much gossip, the train will start immediately.


Speaking of Redis locks, the following three are considered to be the most frequent words:

  • Setnx

  • RedLock

  • Redisson


Setnx


The Setnx command commonly referred to at present does not only refer to the Redis setnx key value command.


Generally refers to the use of the Set command plus the NX parameter in Redis. The Set command currently supports so many optional parameters:

SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]


Of course, I will not write the API silently in the article. The basic parameters are still unclear, so you can jump to the official website.

image.png

The above figure is the general principle of Setnx drawn by the author. It mainly relies on the feature that its Key does not exist to set successfully. Process A obtains the lock. When the key of the lock is not deleted, process B naturally fails to acquire the lock.


So why use PX 30000 to set a timeout period? I'm afraid that the process A is unreasonable and the lock hasn't been released. If it collapses, the lock will be taken away directly, causing no one in the system to get the lock.

image.png

Even so, there is still no guarantee of foolproofness. If process A is unreasonable and the resources in the operation lock exceed the timeout period set by the author, then other processes will get the lock. When Process A comes back, the answer is to delete the locks of other processes, as shown in the figure:

image.png

In the picture just now, the T5 time is changed to the lock timeout, which is released by Redis.


Process B happily gets the lock in T6 and it takes less than a while. Process A finishes the operation and returns a Del to release the lock.


When the operation of process B is completed and the lock is released (at time T8 in the figure):

image.png

In fact, it is not bad that the lock cannot be found. If a process C comes to successfully lock at T7, then process B releases the lock of process C.


By analogy, process C may release the lock of process D, process D.... (Dolls are prohibited), the specific consequences are unknown.


So when using Setnx, although Key is the main function, Value cannot be idle. You can set a unique client ID or use a random number like UUID.


When unlocking, first obtain Value to determine whether it is a lock added by the current process, and then delete it. Fake code:

String uuid = xxxx;
// 伪代码,具体实现看项目中用的连接工具
// 有的提供的方法名为set 有的叫setIfAbsent
set Test uuid NX PX 3000
try{
// biz handle....
finally {
    // unlock
    if(uuid.equals(redisTool.get('Test')){
        redisTool.del('Test');
    }
}


Does it look stable this time? On the contrary, this time the problem is more obvious. In the finally code block, Get and Del are not atomic operations, and there are still process security issues.

image.png

Why are there so many questions? There are two reasons:

  • Only by clarifying the disadvantages can we improve it better.

  • The last piece of code above is still used by many companies.


Paradox of large and small items:

Large companies implement standards, but although small companies and small projects are not rigorous, concurrency is not high, and the probability of problems is as low as that of large companies.


- Lu Xun

image.png

Then one of the correct postures to remove the lock is to use Lua scripts to run through Redis's eval/evalsha commands:

-- lua删除锁:
-- KEYS和ARGV分别是以集合方式传入的参数,对应上文的Test和uuid。
-- 如果对应的value等于传入的uuid。
if redis.call('get', KEYS[1]) == ARGV[1
    then 
    -- 执行删除操作
        return redis.call('del', KEYS[1]) 
    else 
    -- 不成功,返回0
        return 0 
end

The reason for ensuring atomicity through Lua scripts is a bit more popular: even if you write flowers in Lua, the execution is executed by a command (eval/evalsha). If a command is not executed, other clients cannot see it. .


So since it is so troublesome, is there a better tool? Let's talk about Redisson.


Before introducing Redisson, the author briefly explains why the current Setnx default refers to the Set command with NX parameters, instead of directly referring to the Setnx command.


Because Redis version is before 2.6.12, Set does not support NX parameters. If you want to complete a lock, you need two commands:

1. setnx Test uuid
2. expire Test 30


That is, putting in the Key and setting the validity period are two separate steps. In theory, there will be 1 just after execution, the program hangs, and atomicity cannot be guaranteed.


但是早在 2013 年,也就是 7 年前,Redis 就发布了 2.6.12 版本,并且官网(Set 命令页),也早早就说明了“SETNX,SETEX,PSETEX 可能在未来的版本中,会弃用并永久删除”。
笔者曾阅读过一位大佬的文章,其中就有一句指导入门者的面试小套路,具体文字忘记了,大概意思如下:说到 Redis 锁的时候,可以先从 Setnx 讲起,最后慢慢引出 Set 命令的可以加参数,可以体现出自己的知识面。


如果有缘你也阅读过这篇文章,并且学到了这个套路,作为本文的笔者我要加一句提醒:请注意你的工作年限!首先回答官网表明即将废弃的命令,再引出 Set 命令七年前的“新特性”,如果是刚毕业不久的人这么说,面试官会以为自己穿越了。

你套路面试官,面试官也会套路你。  


-- vt・沃兹基硕德


Redisson


Redisson 是 Java 的 Redis 客户端之一,提供了一些 API 方便操作 Redis。

但是 Redisson 这个客户端可有点厉害,笔者在官网截了仅仅是一部分的图:

image.png

这个特性列表可以说是太多了,是不是还看到了一些 JUC 包下面的类名,Redisson 帮我们搞了分布式的版本。


比如 AtomicLong,直接用 RedissonAtomicLong 就行了,连类名都不用去新记,很人性化了。


锁只是它的冰山一角,并且从它的 Wiki 页面看到,对主从,哨兵,集群等模式都支持,当然了,单节点模式肯定是支持的。


本文还是以锁为主,其他的不过多介绍。Redisson 普通的锁实现源码主要是 RedissonLock 这个类,还没有看过它源码的盆友,不妨去瞧一瞧。


源码中加锁/释放锁操作都是用 Lua 脚本完成的,封装的非常完善,开箱即用。这里有个小细节,加锁使用 Setnx 就能实现,也采用 Lua 脚本是不是多此一举?


笔者也非常严谨的思考了一下:这么厉害的东西哪能写废代码?

image.png  

其实笔者仔细看了一下,加锁解锁的 Lua 脚本考虑的非常全面,其中就包括锁的重入性,这点可以说是考虑非常周全,我也随手写了代码测试一下:

image.png

的确用起来像 JDK 的 ReentrantLock 一样丝滑,那么 Redisson 实现的已经这么完善,RedLock 又是什么?


RedLock


image.png

RedLock的中文是直译过来的,就叫红锁。红锁并非是一个工具,而是 Redis 官方提出的一种分布式锁的算法。


就在刚刚介绍完的 Redisson 中,就实现了 RedLock 版本的锁。也就是说除了 getLock 方法,还有 getRedLock 方法。


笔者大概画了一下对红锁的理解:

image.png

如果你不熟悉 Redis 高可用部署,那么没关系。RedLock 算法虽然是需要多个实例,但是这些实例都是独自部署的,没有主从关系。
RedLock 作者指出,之所以要用独立的,是避免了 Redis 异步复制造成的锁丢失,比如:主节点没来的及把刚刚 Set 进来这条数据给从节点,就挂了。


有些人是不是觉得大佬们都是杠精啊,天天就想着极端情况。其实高可用嘛,拼的就是 99.999...% 中小数点后面的位数。


回到上面那张简陋的图片,红锁算法认为,只要 2N+1 个节点加锁成功,那么就认为获取了锁, 解锁时将所有实例解锁。


流程为:

  • 顺序向五个节点请求加锁

  • 根据一定的超时时间来推断是不是跳过该节点

  • 三个节点加锁成功并且花费时间小于锁的有效期

  • 认定加锁成功


也就是说,假设锁 30 秒过期,三个节点加锁花了 31 秒,自然是加锁失败了。


This is just an example. In fact, you shouldn’t wait that long for each node. As the official website says, assuming the validity period is 10 seconds, the timeout period for a single Redis instance operation should be between 5 and 50 milliseconds (note the time unit).


Suppose we set the validity period to 30 seconds, and two Redis nodes have timed out in the figure. Then the node that successfully locked the lock took 3 seconds in total, so the actual validity period of the lock is less than 27 seconds.


That is, the 3 seconds for successfully locking the three instances is deducted, and the total time of waiting for the timeout Redis instance is also deducted. Seeing this, you may have some questions about this algorithm, then you are not alone.


Look back at the description of Red Lock on the Redis official website. At the bottom of this description page, you can see the famous fairy fight about Red Lock.

image.png

The RedLock debate between Martin Kleppmann and Antirez. One is a highly qualified distributed architect, and the other is the father of Redis.
Officially linked people are the most deadly. Just kidding, if the question can be listed on the official website, it must be valuable.


So if you want to use Red Lock in your project, in addition to the introduction of Red Lock, you might as well read two more articles, namely:

  • Questioning post by Martin Kleppmann

  • Antirez's counterattack post


to sum up


After reading so much, have you found out how to achieve it? There is no guarantee of 100% stability. The program is like this, there is no absolute stability, so doing a good job of manual compensation is also an important part, after all: the technology is not enough, the manual is to come together!


Guess you like

Origin blog.51cto.com/14410880/2546318