当前位置:网站首页>Redis based distributed locks and ultra detailed improvement ideas
Redis based distributed locks and ultra detailed improvement ideas
2022-07-06 12:13:00 【A pole】
List of articles
be based on Redis Distributed locks for
What is distributed lock
Meet the visible and mutually exclusive locks of multiple processes in distributed system or cluster mode
Two basic methods to be implemented
Get the lock
Mutually exclusive : Ensure that only one thread can acquire the lock , You can use
setnx
Mutual exclusion ofNon blocking : Try it once , Successfully returns true, Failure to return false
Release the lock
Hand release ,
DEL key
Time out release : Add a timeout when acquiring a lock , Avoid deadlocks caused by service downtime ,
EXPIRE lock 10
Combine
SET lock thread1 NX EX 10 # NX It's mutual exclusion 、EX Is to set the timeout
Realize the idea
The first version of the code
package cn.sticki.common.redis.utils;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/** * @author A pole * @version 1.0 * @date 2022/6/21 21:57 */
public class RedisSimpleLock implements ILock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
private final static String KEY_PREFIX = "lock:";
public RedisSimpleLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
@Override
public boolean tryLock(long timeout) {
// 1. Get thread id
long threadId = Thread.currentThread().getId();
// 2. Try to write redis
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeout, TimeUnit.SECONDS);
// 3. Return the write condition , Success means getting the lock
return Boolean.TRUE.equals(success);
}
@Override
public void unlock() {
// 1. Release the lock
stringRedisTemplate.delete(KEY_PREFIX + name);
}
}
Improve your thinking
The problem is
There is a situation , Execute in the following order :
- Threads 1 Get lock , Start the business , Blocking occurred during execution , In the process of blocking , Lock timeout released
- Threads 2 Get the lock , Because of the thread 1 The lock of has been released overtime , So threads 2 The lock can be successfully obtained
- Threads 1 The business is finished , Release the lock , But threads 1 The lock of has been released for a long time , So the thread is released 2 Lock of
- Threads 3 Get the lock , Threads 3 And thread 2 Concurrent execution , At this time, the lock does not play its due role ...
Sketch Map :
Solution
This problem , In fact, it releases the lock that is not generated by itself , So we can use specific signs , Before releasing the lock, judge whether the lock is generated by itself , And only release the lock generated by yourself .
Threads can be id Deposit in value, Judge the lock before releasing value Is it equal to your own thread id, If equal to, it indicates that the lock is generated by the current thread , You can release .
The new problem
If I have multiple servers , Forming a cluster , Then different servers may have threads id The same thing , It will lead to value identical , Thus, the lock of others is released by mistake .
Solution
Let each started service have a different identity , Then splice threads id, And then we can solve this problem .
Final plan
Store the thread ID when acquiring the lock ( It can be used UUID + Threads id Express )
Get the thread ID in the lock first when releasing the lock , Determine whether it is consistent with the current thread ID
If consistent, release the lock
If not, do not release the lock
The second version of the code
package cn.sticki.common.redis.utils;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/** * @author A pole * @version 1.0 * @date 2022/6/21 21:57 */
public class RedisSimpleLock implements ILock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
private final static String KEY_PREFIX = "lock:";
private final static String KEY_UUID = UUID.randomUUID() + ":";
public RedisSimpleLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
@Override
public boolean tryLock(long timeout) {
// 1. Generate key, By splicing prefix and business name
String key = KEY_PREFIX + name;
// 2. Generate value, Used to determine whether the lock is generated by the current thread . Use random UUID+ Current thread id, Prevent clustering value Collision .
String value = KEY_UUID + Thread.currentThread().getId();
// 3. Try to write redis
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
// 4. Return the write condition , Success means getting the lock
return Boolean.TRUE.equals(success);
}
@Override
public void unlock() {
String key = KEY_PREFIX + name;
String value = KEY_UUID + Thread.currentThread().getId();
// 1. Get the value of the lock
String lockValue = stringRedisTemplate.opsForValue().get(key);
if (value.equals(lockValue)) {
// 2. If the values are the same , Then the current lock is created by the current thread , You can delete
stringRedisTemplate.delete(key);
}
}
}
The new problem
If , I mean, if , Above unlock() In the code , After obtaining the value of the lock , Delete key Before , There's a blockage (GC Blocking ), When the blocking is complete , The lock created by the current thread has been released , Then something similar to the above happened , It will also lead to lock failure .
Maybe the description is not very clear , Look at the diagram :
Solutions
The main reason for this problem is that judging the lock ID and releasing the lock are two operations performed separately , Solve this problem , Can pass Lua The script binds the two operations together .
Redis Of Lua Script
Redis Provides Lua Script function , Write multiple in one script Redis command , Ensure atomicity when multiple commands are executed .Lua Is a programming language , Its basic syntax can be referred to the website :https://www.runoob.com/lua/lua-tutorial.html
Here we focus on Redis Provided calling function , The grammar is as follows :
# perform redis command
redis.call(' Command name ', 'key', ' Other parameters ', ...)
for example , We have to carry out set name jack, Then the script is like this :
# perform set name jack
redis.call('set', 'name', 'jack')
for example , We need to execute first set name Rose, Re execution get name, The script is as follows :
# Execute first set name jack
redis.call('set', 'name', 'jack')
# Re execution get name
local name = redis.call('get', 'name')
# return
return name
Third edition code
use Lua Write release lock
The business process of releasing locks is like this :
- Get the thread ID in the lock
- Judge whether it is consistent with the specified mark ( The current thread is marked ) Agreement
- If consistent, release the lock ( Delete )
- If not, do nothing
unlock.lua( This document is in mian/resource below )
-- there KEYS[1] It's locked key, there ARGV[1] Is the current thread identifier
-- Get the mark in the lock , Determine whether it is consistent with the current thread mark
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
-- Agreement , Then delete the lock
return redis.call('DEL', KEYS[1])
end
-- atypism , Then return directly
return 0
Java The code is as follows
package cn.sticki.common.redis.utils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/** * @author A pole * @version 1.0 * @date 2022/6/21 21:57 */
public class RedisSimpleLock implements ILock {
private final static String KEY_PREFIX = "lock:";
private final static String KEY_UUID = UUID.randomUUID() + ":";
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
// load lua file
UNLOCK_SCRIPT = new DefaultRedisScript<>();
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
UNLOCK_SCRIPT.setResultType(Long.class);
}
private final StringRedisTemplate stringRedisTemplate;
private final String name;
public RedisSimpleLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
@Override
public boolean tryLock(long timeout) {
// 1. Generate key, By splicing prefix and business name
String key = KEY_PREFIX + name;
// 2. Generate value, Used to determine whether the lock is generated by the current thread . Use random UUID+ Current thread id, Prevent clustering value Collision .
String value = KEY_UUID + Thread.currentThread().getId();
// 3. Try to write redis
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
// 4. Return the write condition , Success means getting the lock
return Boolean.TRUE.equals(success);
}
@Override
public void unlock() {
String key = KEY_PREFIX + name;
String value = KEY_UUID + Thread.currentThread().getId();
// call lua Script
stringRedisTemplate.execute(
UNLOCK_SCRIPT,
Collections.singletonList(key),
value);
}
}
summary
Realize the idea :
- utilize set nx ex Get the lock , And set expiration time , Save thread id
- When releasing the lock, first judge whether the thread mark is consistent with itself , If it is consistent, delete the lock
- utilize set nx Satisfy the mutual exclusion
- utilize set ex Ensure that the lock can still be released in case of failure , Avoid deadlock , Improve safety
- utilize Redis The cluster guarantees high availability and high concurrency
Points for improvement :
- Do not reenter : The same thread cannot acquire the same lock more than once
- Can't try again : Only one attempt to acquire a lock returns false, No retry mechanism
- Time out release : Lock timeout release can avoid deadlock , But if the business takes a long time to execute , It will also cause the lock to release , There are safety risks
- Master slave consistency : If Redis Provides a master-slave cluster , There is a delay in master-slave synchronization , When the main goes down , If you slave and synchronize the lock data in the master , The lock will fail
introduce Redisson
Redisson It's a Redis On the basis of implementation Java In memory data grid (In-Memory Data Grid). It not only provides a series of distributed Java Common objects , There are also many distributed services , It includes the implementation of various distributed locks .
Official website address : https://redisson.org
GitHub Address : https://github.com/redisson/redisson
Postscript
This article is a summary and notes I made when learning from the video course of dark horse programmer , Interested students can watch the video by themselves :https://www.bilibili.com/video/BV1cr4y1671t?p=56
边栏推荐
- Fashion-Gen: The Generative Fashion Dataset and Challenge 论文解读&数据集介绍
- OPPO VOOC快充电路和协议
- RuntimeError: cuDNN error: CUDNN_ STATUS_ NOT_ INITIALIZED
- ES6语法总结--上篇(基础篇)
- Bubble sort [C language]
- PyTorch四种常用优化器测试
- 基于Redis的分布式ID生成器
- C language, log print file name, function name, line number, date and time
- gcc 编译选项
- Common properties of location
猜你喜欢
level16
Machine learning -- linear regression (sklearn)
Apprentissage automatique - - régression linéaire (sklearn)
Basic knowledge of lithium battery
Navigator object (determine browser type)
Several declarations about pointers [C language]
ES6语法总结--下篇(进阶篇 ES6~ES11)
Kconfig Kbuild
荣耀Magic 3Pro 充电架构分析
STM32 如何定位导致发生 hard fault 的代码段
随机推荐
Esp8266 uses Arduino to connect Alibaba cloud Internet of things
Programmers can make mistakes. Basic pointers and arrays of C language
基于Redis的分布式ID生成器
Dependency in dependencymanagement cannot be downloaded and red is reported
Reno7 60W超级闪充充电架构
列表的使用
Implementation scheme of distributed transaction
C语言,log打印文件名、函数名、行号、日期时间
E-commerce data analysis -- salary prediction (linear regression)
Basic knowledge of lithium battery
RT-Thread API参考手册
The dolphin scheduler remotely executes shell scripts through the expect command
高通&MTK&麒麟 手機平臺USB3.0方案對比
I2C总线时序详解
ESP learning problem record
Use of lists
Reading notes of difficult career creation
Vscode basic configuration
arduino获取数组的长度
电商数据分析--薪资预测(线性回归)