当前位置: 首页 > 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;轻松创建个性化的动画内容. 以上是官网的图, 下边是本地部署生成的,效果差…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

Python学习(8) ----- Python的类与对象

Python 中的类&#xff08;Class&#xff09;与对象&#xff08;Object&#xff09;是面向对象编程&#xff08;OOP&#xff09;的核心。我们可以通过“类是模板&#xff0c;对象是实例”来理解它们的关系。 &#x1f9f1; 一句话理解&#xff1a; 类就像“图纸”&#xff0c;对…...