当前位置: 首页 > news >正文

使用Lua脚本保证原子性的Redis分布式锁实现

这是原来的代码:

@Override
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);}
}

这段代码的主要问题在于获取和删除锁的操作不是原子性的。具体来说,这段代码先获取当前锁的标识,然后检查是否与当前线程的标识一致,如果一致则删除锁。然而,在这两个操作之间可能存在时间差,导致潜在的竞争。

为什么使用Lua脚本和Redis?

Redis支持Lua脚本,以便在单个原子操作中执行多个命令。这对于分布式锁来说至关重要,因为需要在一次操作中检查条件并执行操作,以防止竞争条件。

用于解锁的Lua脚本

以下是用于确保释放锁时原子性的Lua脚本:

-- 比较线程标识与锁中的标识是否一致
if(redis.call("get",KEYS[1])==ARGV[1]) then-- 释放锁,删除键return redis.call("del",KEYS[1])
end
return 0

Java代码实现

下面是Java代码,用于实现Redis锁:

public class SimpleRedisLock implements Ilock {private String name;private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;}private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;static {UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}@Overridepublic boolean tryLock(long timeoutSec) {// 获取线程标识String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec,TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {// 调用lua脚本stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());}
}

通过使用Redis和Lua脚本,我们可以实现一个简单但高效的分布式锁,确保锁操作的原子性。这种方法可以有效防止并发问题,是构建分布式系统的重要工具。

相关文章:

  • gcn+tcn+transformer入侵检测
  • Java基础 - 练习(五)根据今天日期获取一周内的日期(基姆拉尔森公式)
  • SAP AI Copilot Joule有可能是对SAP顾问的王炸
  • python pyautogui实现图片识别点击失败后重试
  • 【linux】操作系统使用wget下载网络文件,内核tcpv4部分运行日志
  • 【ClickHouse】副本、分片集群 (六)
  • 随机产生一些江河上的坐标数据
  • 秋招突击——6/17——复习{整理昨天的面试资料}——新作{删除链表倒数第n个节点}
  • Jmeter多个请求按照比例并发压测的几种方式
  • POI:接收上传上来的excel,解析并导入到数据库
  • Kafka中的时间轮算法
  • 2024广东省职业技能大赛云计算赛项实战——Ansible部署Zabbix
  • error: the type ‘const zjloc::<lambda(const Vec2i, const Vec2i)>’
  • JAVA NIO(二) Buffer和Channel
  • Elasticsearch:倒数排序融合 - Reciprocal rank fusion - 8.14
  • 自己简单写的 事件订阅机制
  • [case10]使用RSQL实现端到端的动态查询
  • Java精华积累:初学者都应该搞懂的问题
  • js写一个简单的选项卡
  • PaddlePaddle-GitHub的正确打开姿势
  • vue--为什么data属性必须是一个函数
  • 从零搭建Koa2 Server
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 前端学习笔记之观察者模式
  • 小程序开发之路(一)
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • ‌JavaScript 数据类型转换
  • #565. 查找之大编号
  • #NOIP 2014#Day.2 T3 解方程
  • $ git push -u origin master 推送到远程库出错
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (02)Hive SQL编译成MapReduce任务的过程
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (六)vue-router+UI组件库
  • (十一)图像的罗伯特梯度锐化
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)面试需要掌握的技巧
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • .libPaths()设置包加载目录
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .Net 8.0 新的变化
  • .Net Core 微服务之Consul(三)-KV存储分布式锁
  • .NET 某和OA办公系统全局绕过漏洞分析
  • .NET/C#⾯试题汇总系列:⾯向对象
  • .Net6 Api Swagger配置
  • .NET多线程执行函数
  • @Transactional事务注解内含乾坤?
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)