Redis的分布式锁问题九Redis + Lua 脚本实现分布式锁
Redis的分布式锁问题(九)Redis Lua 脚本实现分布式锁
Redis的分布式锁问题(九)Redis Lua 脚本实现分布式锁
上集回顾
但是这仍然不是最佳的实现方案,它在极端的情况下还是会发生问题!
-
public void unlock() {
-
// 获取线程标示
-
String threadId = ID_PREFIX Thread.currentThread().getId();
-
// 获取锁中的标示
-
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX name);
-
// 判断标示是否一致
-
if(threadId.equals(id)) {
-
// 释放锁
-
stringRedisTemplate.delete(KEY_PREFIX name);
-
}
-
}
如上述代码,如果“判断锁标识”和“释放锁”,之间发生了阻塞呢?(JVM触发FULL GC)
那么之前一节所讲的“锁误删”就有可能发生!!!
所以,我们的解决思路是要保证这两个操作的原子性!
我们可以使用Redis的事务 乐观锁来解决这个问题,但是这样子做非常复杂!这里我们使用 Lua 脚本来实现分布式锁
Lua的简单介绍
Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。
Lua 教程 | 菜鸟教程 (runoob.com)https://www.runoob.com/lua/lua-tutorial.html
-
if(0)
-
then
-
print("0 为 true")
-
end
redis调用函数
set name jack
set name Rose,再执行get name
redis的 EVAL 命令
如果脚本中的key、value不想写死,可以作为参数传递。key类型参数会放入KEYS数组,其它参数会放入ARGV数组,在脚本中可以从KEYS和ARGV数组获取这些参数:
在Lua中,数组的下标是从1开始的!!!
Lua脚本解决unLock业务流程
- 获取锁中的线程标示
- 判断是否与指定的标示(当前线程标示)一致
- 如果一致则释放锁(删除)
- 如果不一致则什么都不做
代码实现
unLock.lua
-
-- 获取锁标识,是否与当前线程一致?
-
if(redis.call('get', KEYS[1]) == ARGV[1]) then
-
-- 一致,删除
-
return redis.call('del', KEYS[1])
-
end
-
-- 不一致,直接返回
-
return 0
RedisTemplate调用Lua脚本的API
Lua 解决unLock问题
-
/**
-
* redis的分布式锁
-
* 实现ILock接口
-
*/
-
public class SimpleRedisLock implements ILock {
-
-
// 不同的业务有不同的锁名称
-
private String name;
-
private StringRedisTemplate stringRedisTemplate;
-
private static final String KEY_PREFIX = "lock:";
-
private static final String ID_PREFIX = UUID.randomUUID().toString(true) "-";
-
// DefaultRedisScript,
-
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
-
-
public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
-
this.name = name;
-
this.stringRedisTemplate = stringRedisTemplate;
-
}
-
-
// 初始化 UNLOCK_SCRIPT,用静态代码块的方式,一加载SimpleRedisLock有会加载unlock.lua
-
// 避免每次调unLock() 才去加载,提升性能!!!
-
static {
-
UNLOCK_SCRIPT = new DefaultRedisScript<>();
-
// setLocation() 设置脚本位置
-
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
-
// 返回值类型
-
UNLOCK_SCRIPT.setResultType(Long.class);
-
}
-
-
/**
-
* 获取锁
-
*/
-
-
public boolean tryLock(long timeoutSec) {
-
// 获取线程标示
-
String threadId = ID_PREFIX Thread.currentThread().getId();
-
// 获取锁
-
// set lock thread1 nx ex 10
-
// nx : setIfAbsent(如果不存在) , ex : timeoutSec(秒)
-
Boolean success = stringRedisTemplate.opsForValue()
-
.setIfAbsent(KEY_PREFIX name, threadId, timeoutSec, TimeUnit.SECONDS);
-
// 自动拆箱(Boolean -> boolean)!!!可能有风险
-
return Boolean.TRUE.equals(success);
-
}
-
-
/**
-
* 解决判断(锁标识、释放锁)这两个动作,之间产生阻塞!!!
-
* JVM的 FULL GC
-
* 要让这两个动作具有原子性
-
*/
-
-
public void unlock() {
-
// 调用lua脚本
-
stringRedisTemplate.execute(
-
UNLOCK_SCRIPT,
-
Collections.singletonList(KEY_PREFIX name),
-
ID_PREFIX Thread.currentThread().getId());
-
}
-
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgafjef
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13