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

【JAVA高级】如何使用Redis加锁和解锁(一)、Lua脚本执行原理及流程

文章目录

    • 加锁
      • 方法一:使用SETNX命令结合EXPIRE命令
      • 方法二:使用SET命令的扩展参数(NX和PX)
      • 方法三:使用Lua脚本
    • 解锁
      • 方法一:简单删除key
      • 方法二:使用Lua脚本验证后删除key
    • Lua脚本的执行原理:
      • 执行原理
      • 原子性保证
      • 注意事项
      • Lua示例:解锁

在Redis中加锁和解锁通常是通过Redis的原子性命令来实现的,以保证操作的原子性和线程安全。
以下是在Redis中加锁和解锁的详细步骤和注意事项:

加锁

方法一:使用SETNX命令结合EXPIRE命令

1.SETNX命令:SETNX是“SET if Not eXists”的缩写,它会在指定的key不存在时设置key的值。如果key已经存在,则命令不执行任何操作。

  • 命令格式:SETNX key value
  • 如果key不存在,设置key的值并返回1(表示加锁成功)。
  • 如果key已存在,不做任何操作并返回0(表示加锁失败)。

2.EXPIRE命令:为防止死锁,需要在加锁后设置一个过期时间。

  • 命令格式:EXPIRE key seconds
  • 设置key的过期时间,单位为秒。

注意:由于SETNX和EXPIRE是两个命令,它们之间可能存在时间差,这可能导致在SETNX和EXPIRE命令之间,Redis服务器崩溃或其他问题导致锁无法正确释放。因此,这种方法存在潜在的安全隐患。

方法二:使用SET命令的扩展参数(NX和PX)

Redis 2.6.12及以上版本支持SET命令的NX和PX选项,可以一次性完成加锁和设置过期时间的操作,从而避免上述安全隐患。

命令格式:SET key value NX PX milliseconds

  • NX:只在key不存在时设置key的值。
  • PX:设置key的过期时间,单位为毫秒。
    如果命令执行成功,则表示加锁成功;如果因为key已存在而执行失败,则表示加锁失败

方法三:使用Lua脚本

Lua脚本可以确保加锁和设置过期时间的原子性。

local lockKey = KEYS[1]  
local lockValue = ARGV[1]  
local lockTime = tonumber(ARGV[2])  
if redis.call('setnx', lockKey, lockValue) == 1 then  redis.call('expire', lockKey, lockTime)  return 1  
else  return 0  
end

使用EVAL命令执行Lua脚本。

解锁

解锁操作通常是通过删除Redis中的key来实现的。但是,直接删除key可能会存在安全风险,因为任何客户端都可以删除key,从而解锁。因此,解锁操作通常需要验证当前客户端是否是锁的持有者。

方法一:简单删除key

如果不考虑安全因素,可以直接使用DEL命令删除key来解锁。

  • 命令格式:DEL key
    但是,这种方法不推荐在生产环境中使用,因为它无法验证锁的持有者。

方法二:使用Lua脚本验证后删除key

为了确保解锁的安全性,可以使用Lua脚本来验证当前客户端是否是锁的持有者,然后再删除key。

-- KEYS[1] 是锁的key  
-- ARGV[1] 是锁的持有者(即客户端的唯一标识符)  local lockKey = KEYS[1]  
local lockValue = ARGV[1]  -- 检查锁是否存在,并且锁的值是否与客户端提供的值相匹配  
if redis.call('get', lockKey) == lockValue then  -- 如果匹配,则删除锁  return redis.call('del', lockKey)  
else  -- 如果不匹配,则不执行任何操作,并返回0表示解锁失败  return 0  
end

使用EVAL命令执行Lua脚本。
这种方法通过验证锁的值来确保只有锁的持有者才能解锁,从而提高了系统的安全性。

在Redis中加锁和解锁时,应优先考虑使用SET命令的扩展参数(NX和PX)或Lua脚本来确保操作的原子性和安全性。同时,为了避免死锁的发生,应为锁设置合理的过期时间。在解锁时,应验证锁的持有者身份,确保只有锁的持有者才能解锁。

Lua脚本的执行原理:

Lua脚本在Redis中的实现原理主要依赖于Redis服务器对Lua脚本的支持。Redis从2.6版本开始引入了Lua脚本功能,允许用户将一系列Redis命令封装在Lua脚本中,然后一次性发送给Redis服务器执行。这种方式有几个重要的优点,包括减少网络开销、保证命令的原子性执行以及简化客户端逻辑。

执行原理

  • 脚本发送:
    客户端将Lua脚本以字符串的形式发送给Redis服务器。脚本中可以包含任意数量的Redis命令,这些命令会按照脚本中的顺序执行。
  • 脚本加载:
    Redis服务器接收到Lua脚本后,会将其加载到内存中。这一步主要是将脚本字符串存储起来,以便后续执行。
  • 脚本执行:
    当需要执行脚本时,Redis服务器会启动一个Lua环境(通常是基于LuaJIT或标准Lua解释器),并将脚本字符串传递给这个环境进行执行。在脚本执行期间,Redis服务器会暂停处理其他客户端的命令(或者将它们排入队列),以确保脚本的原子性执行。
  • 脚本内部操作:
    Lua脚本内部可以使用Redis提供的Lua库来执行Redis命令。这些命令会被封装成Lua函数,脚本可以直接调用这些函数来与Redis数据库进行交互。例如,脚本可以使用redis.call()函数来执行Redis命令,并获取命令的返回结果。
  • 结果返回:
    脚本执行完毕后,Lua环境会将脚本的最后一个返回值(或所有返回值,取决于客户端的请求)返回给Redis服务器。Redis服务器再将这个值(或这些值)发送给客户端。
  • 脚本清理:
    如果脚本执行成功并返回了结果,Redis服务器会清理与脚本相关的资源,包括Lua环境中的变量和Redis命令的执行结果等。如果脚本执行过程中发生了错误,Redis服务器会记录错误信息,并可能将错误信息返回给客户端。

原子性保证

Lua脚本在Redis中的执行是原子的,这意味着在脚本执行期间,Redis服务器不会处理其他客户端的命令。这种原子性保证是通过Redis服务器内部的机制来实现的,具体来说,Redis服务器在执行Lua脚本时会使用一种称为“脚本锁”的机制来阻塞其他客户端的命令。

这种原子性保证对于实现分布式锁等需要高度一致性的操作非常重要。通过使用Lua脚本,我们可以确保在加锁和解锁的过程中,Redis命令的执行不会被其他客户端的命令打断,从而避免了竞态条件的发生。

注意事项

  • 脚本超时:Redis允许为Lua脚本设置最大执行时间(通过lua-time-limit配置项),以防止脚本执行时间过长导致Redis服务器无响应。如果脚本执行时间超过了限制,Redis服务器将中断脚本的执行并返回错误。
  • 内存使用:Lua脚本在Redis服务器中执行时会占用一定的内存资源。如果脚本过大或过于复杂,可能会导致Redis服务器的内存使用过高。因此,在编写Lua脚本时需要注意内存的使用情况。
  • 脚本缓存:Redis会将已经加载的Lua脚本缓存起来,以便后续再次执行时可以直接使用缓存的脚本字符串,而不需要重新发送脚本内容。这有助于减少网络开销和提高执行效率。但是,如果缓存的脚本过多,也可能会占用较多的内存资源。因此,在需要时可以通过SCRIPT FLUSH命令来清空脚本缓存。

Lua示例:解锁

-- KEYS[1] 是锁的key  
-- ARGV[1] 是锁的持有者(即客户端的唯一标识符)  local lockKey = KEYS[1]  
local lockValue = ARGV[1]  -- 检查锁是否存在,并且锁的值是否与客户端提供的值相匹配  
if redis.call('get', lockKey) == lockValue then  -- 如果匹配,则删除锁  return redis.call('del', lockKey)  
else  -- 如果不匹配,则不执行任何操作,并返回0表示解锁失败  return 0  
end

在Redis客户端中,你可以使用如下命令来执行这个Lua脚本:

-- 假设锁的key是"mylock",锁的持有者标识符是"myuniquevalue"  
EVAL "local lockKey = KEYS[1]; local lockValue = ARGV[1]; if redis.call('get', lockKey) == lockValue then return redis.call('del', lockKey) else return 0 end" 1 mylock myuniquevalue

这里,EVAL命令用于执行Lua脚本,1表示脚本中KEYS数组的长度(在这个例子中,我们只有一个key),mylock是传递给脚本的key,myuniquevalue是传递给脚本的持有者标识符。

如果脚本返回1,则表示锁已成功解锁;如果返回0,则表示锁不存在或当前客户端不是锁的持有者,因此无法解锁。

相关文章:

【JAVA高级】如何使用Redis加锁和解锁(一)、Lua脚本执行原理及流程

文章目录 加锁方法一:使用SETNX命令结合EXPIRE命令方法二:使用SET命令的扩展参数(NX和PX)方法三:使用Lua脚本 解锁方法一:简单删除key方法二:使用Lua脚本验证后删除key Lua脚本的执行原理&#…...

2024年使用宝塔面板轻松部署Java Web

以下是2024年最新图形化部署Java Web项目到CentOS系统的手把手教程: 一、准备工作 确保服务器环境:确保你的服务器已经安装了CentOS 7操作系统,并且已经安装了宝塔面板。如果还没有安装,可以参考之前的教程进行安装。下载Java W…...

闯关训练一:Linux基础

闯关任务:完成SSH连接与端口映射并运行hello_world.py 1.创建开发机 2.SSH连接 3. VS-Code 连接 选择 Linux 平台 ,输入密码 ,选择进入文件夹 4.端口映射 按照下文安装Docs pip install gradio 运行server.py import gradio as grdef …...

鸿蒙NEXT开发-ArkTS(基于最新api12稳定版)

注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下 如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识 专栏地址: https://blog.csdn.net/qq_56760790/…...

laravel延迟队列 取消未支付超时订单订单

1:生成待支付订单时,调用延迟队列 超过十五分钟未支付自动取消 use App\Jobs\endTask; use Illuminate\Support\Carbon; $resPost1 array("act" > "cy_order_cancel", "id" > $id); endTask::dispatch($resPos…...

解锁高效开发的秘密武器

在当今这个信息爆炸、技术日新月异的时代,编程工具的选择对于开发者来说至关重要。一个好的编程工具不仅能够简化代码编写,还能自动化任务、提升调试速度,甚至让团队协作更加顺畅。那么,哪款编程工具能让你的工作效率翻倍呢&#…...

【CSS】兼容处理

兼容前缀兼容查询 由于不同浏览器对CSS标准的支持程度不同,可能会导致在不同浏览器中出现样式差异。为了解决这个问题,需要采取一些措施来提高CSS的兼容性 兼容前缀 兼容前缀针对的浏览器-webkit-WebKit 内核浏览器,如:Safari 、…...

C语言线程

线程 多个进程中通过轮流使用CPU来完成自己的任务,如果多个进程的操作都一模一样那么CPU的开销就会很大,因为进程的地址都是私有的,如果CPU对相同的操作只执行一次,后面再遇到直接去获取即可,这样大大降低了CPU的开销…...

自闭症寄宿学校 vs. 日常教育:为孩子提供更多可能

在探索自闭症儿童的教育路径时,家长们往往面临一个重大的选择:是选择传统的日常教育环境,还是寻找专为自闭症儿童设计的寄宿学校?广州的星贝育园自闭症儿童寄宿制学校,以其独特的教育模式和全方位的关怀体系&#xff0…...

RxSwift系列(二)操作符

一、变换操作符:buffer、map、compactMap等 1.buffer buffer方法作用是缓冲组合,第一个参数是缓冲时间,第二个参数是缓冲个数,第三个参数是线程。缓存 Observable 中发出的新元素,当元素达到某个数量,或者…...

Gin框架简易搭建(3)--Grom与数据库

写在前面 项目地址 个人认为GORM 指南这个网站是相比较之下最为清晰的框架介绍 但是它在环境搭建阶段对于初学者而言不是很友好,尤其是使用mysql指令稍有不同,以及更新的方法和依赖问题都是很让人头疼的,而且这些报错并非逻辑上的&#xf…...

JavaScript模块化-CommonJS规范和ESM规范

1 ES6模块化 1.1 ES6基本介绍 ES6 模块是 ECMAScript 2015(ES6)引入的标准模块系统,广泛应用于浏览器环境下的前端开发。Node.js环境主要使用CommonJS规范。ESM使用import和export来实现模块化开发从而解决了以下问题: 全局作用…...

解决银河麒麟V10中的apt Lock异常

解决银河麒麟V10中的apt Lock异常 一、查找并杀掉apt进程二、删除锁文件三、重新尝试apt命令 💖The Begin💖点点关注,收藏不迷路💖 在使用银河麒麟V10的apt命令时,如果遇到lock异常,可以按以下步骤解决&…...

windows11环境安装lua及luarocks(踩坑篇)

一、lua安装及下载 官方地址: Lua Binaries Download 从这里就有坑了,下载后先解压win64_bin.zip,之后解压lib,用lib中的文件替换win64的,并把include文件夹复制过去,之后复制并重命名lua54,方…...

Glide基本用法及With方法源码解析

文章目录 引入优点 使用步骤导入依赖权限使用 其他用法占位符错误图片后备回调符圆角过渡动画大小调整gif缩略图 使用RequestOptions缓存机制设置缓存策略清理缓存 使用集成库OkHttpVolley with源码解析getRetrieverGlide.getinitializeGlide getRequestManagerRetriever Reque…...

html中的文本标签(含标签的实现案例)

目录 1.标题标签 2.标题标签的align属性 3.段落标签 4.水平线标签hr 5.换行标签br 6.文本样式标签font ​编辑7.文本格式化标签 8.文本语义标签 1)时间time标签 2)文本高亮Mark标签 3)cite标签 9.特殊字符标签 10.图像标签img 附录&#xff…...

通信协议感悟

本文结合个人所学,简要讲述SPI,I2C,UART通信的特点,限制。 1.同步通信 UART,SPI,I2C三种串行通讯方式,SPI功能引脚为CS,CLK,MOSI,MISO;I2C功能引…...

IDEA几大常用AI插件

文章目录 前言列表GPT中文版TalkXBito AIIDEA自带的AI 前言 最近AI、GPT特别火,IDEA里面又有一堆插件支持GPT,所以做个专题比较一下各个GPT插件 列表 先看idea的plugins里支持哪些,搜索“GPT”之后得到的,我用下来感觉第一第二和…...

51单片机学习第六课---B站UP主江协科技

DS18B20 1、基本知识讲解 2、DS18B20读取温度值 main.c #include<regx52.h> #include"delay.h" #include"LCD1602.h" #include"key.h" #include"DS18B20.h"float T; void main () {LCD_Init();LCD_ShowString(1,1,"temp…...

sadTalker本地编译

SadTalker一款开源的可生成逼真的人像动画的工具。它利用深度学习技术&#xff0c;根据输入的图像和音频&#xff0c;生成具有生动表情和动作的视频。用户可以通过上传照片或使用预设的模型&#xff0c;轻松创建个性化的动画内容. 以上是官网的图, 下边是本地部署生成的,效果差…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

JavaScript 标签加载

目录 JavaScript 标签加载script 标签的 async 和 defer 属性&#xff0c;分别代表什么&#xff0c;有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...

用鸿蒙HarmonyOS5实现国际象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码&#xff0c;使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...

Web APIS Day01

1.声明变量const优先 那为什么一开始前面就不能用const呢&#xff0c;接下来看几个例子&#xff1a; 下面这张为什么可以用const呢&#xff1f;因为复杂数据的引用地址没变&#xff0c;数组还是数组&#xff0c;只是添加了个元素&#xff0c;本质没变&#xff0c;所以可以用con…...

汇编语言学习(三)——DoxBox中debug的使用

目录 一、安装DoxBox&#xff0c;并下载汇编工具&#xff08;MASM文件&#xff09; 二、debug是什么 三、debug中的命令 一、安装DoxBox&#xff0c;并下载汇编工具&#xff08;MASM文件&#xff09; 链接&#xff1a; https://pan.baidu.com/s/1IbyJj-JIkl_oMOJmkKiaGQ?pw…...