lua脚本实现redis分布式锁(脚本解析)

news2024/12/25 9:12:50

文章目录

  • lua介绍
    • lua基本语法
    • redis执行lua脚本 - EVAL指令
    • 使用lua保证删除原子性

lua介绍

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

设计目的

​ 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 特性

  • 轻量级:它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展:Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性:
    • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
    • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
    • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

lua基本语法

对lua脚本感兴趣,请移步到官方教程或者 《菜鸟教程》。这里仅以redis中可能会用到的部分语法作介绍。

a = 5               -- 全局变量
local b = 5         -- 局部变量, redis只支持局部变量
a, b = 10, 2*x      -- 等价于       a=10; b=2*x

流程控制:

if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

redis执行lua脚本 - EVAL指令

在redis中需要通过eval命令执行lua脚本。

格式:

EVAL script numkeys key [key ...] arg [arg ...]
script:lua脚本字符串,这段Lua脚本不需要(也不应该)定义函数。
numkeys:lua脚本中KEYS数组的大小
key [key ...]:KEYS数组中的元素
arg [arg ...]:ARGV数组中的元素

案例1:基本案例

EVAL "return 10" 0

输出:(integer) 10

案例2:动态传参

EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 5 10 20 30 40 50 60 70 80 90
# 输出:10 20 60 70

EVAL "if KEYS[1] > ARGV[1] then return 1 else return 0 end" 1 10 20
# 输出:0

EVAL "if KEYS[1] > ARGV[1] then return 1 else return 0 end" 1 20 10
# 输出:1

传入了两个参数10和20,KEYS的长度是1,所以KEYS中有一个元素10,剩余的一个20就是ARGV数组的元素。

redis.call()中的redis是redis中提供的lua脚本类库,仅在redis环境中可以使用该类库。

案例3:执行redis类库方法

set aaa 10  -- 设置一个aaa值为10
EVAL "return redis.call('get', 'aaa')" 0
# 通过return把call方法返回给redis客户端,打印:"10"

注意:**脚本里使用的所有键都应该由 KEYS 数组来传递。**但并不是强制性的,代价是这样写出的脚本不能被 Redis 集群所兼容。

案例4:给redis类库方法动态传参

EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 bbb 20

在这里插入图片描述

学到这里基本可以应付redis分布式锁所需要的脚本知识了。

案例5:pcall函数的使用(了解)

-- 当call() 在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,输出错误信息
EVAL "return redis.call('sets', KEYS[1], ARGV[1]), redis.call('set', KEYS[2], ARGV[2])" 2 bbb ccc 20 30
-- pcall函数不影响后续指令的执行
EVAL "return redis.pcall('sets', KEYS[1], ARGV[1]), redis.pcall('set', KEYS[2], ARGV[2])" 2 bbb ccc 20 30

注意:set方法写成了sets,肯定会报错。

在这里插入图片描述

使用lua保证删除原子性

删除LUA脚本:

if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end

代码实现:

public void deduct() {
    String uuid = UUID.randomUUID().toString();
    // 加锁setnx
    while (!this.redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS)) {
        // 重试:循环
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    try {
        // this.redisTemplate.expire("lock", 3, TimeUnit.SECONDS);
        // 1. 查询库存信息
        String stock = redisTemplate.opsForValue().get("stock").toString();

        // 2. 判断库存是否充足
        if (stock != null && stock.length() != 0) {
            Integer st = Integer.valueOf(stock);
            if (st > 0) {
                // 3.扣减库存
                redisTemplate.opsForValue().set("stock", String.valueOf(--st));
            }
        }
    } finally {
        // 先判断是否自己的锁,再解锁
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
            "then " +
            "   return redis.call('del', KEYS[1]) " +
            "else " +
            "   return 0 " +
            "end";
        this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList("lock"), uuid);
    }
}

压力测试,库存量也没有问题,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1185113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最适合后端程序员要学的前端基本知识js版本

本文章适合有后端基础&#xff0c;也对前端知识有所了解的同学&#xff0c;简单而精准的了解后端程序员要了解的前端知识。让自己能看懂前端js&#xff0c;html和css版本后续写出。。。。 自定义对象 在对象中创建对象的行为时若是要调用对象属性&#xff0c;要加this 列如&a…

kubernetes 认证授权

目录 一、kubernetes API访问控制 二、pod绑定sa 三、认证 四、授权 一、kubernetes API访问控制 Authentication&#xff08;认证&#xff09;认证方式现共有8种&#xff0c;可以启用一种或多种认证方式&#xff0c;只要有一种认证方式通过&#xff0c;就不再进行其它方式…

【Axure高保真原型】树切换动态面板案例

今天和大家分享树切换动态面板的原型模板&#xff0c;点击树的箭头可以打开或者收起子节点&#xff0c;点击最后一级人物节点&#xff0c;可以切换右侧面板的状态到对应的页面&#xff0c;左侧的树是通过中继器制作的&#xff0c;使用简单&#xff0c;只需要按要求填写中继器表…

人大金仓三大兼容:SQL Server迁移无忧

SQL Server在数据库领域一直占据着重要地位。作为一款成熟稳定的关系型数据库管理系统&#xff0c;SQL Server在国内有着广泛的用户群体&#xff0c;医疗、海关、政务等行业的核心业务系统多采用SQL Server数据库。随着政策与市场的双重驱动&#xff0c;信息技术应用创新产业的…

【快速使用ShardingJDBC的哈希分片策略进行分表】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f34a;1.引入maven依赖&#x1f34a;2.启动类上添加注解MapperScan&#x1f34a;3.添加application.properties配置&#x1f34a;4.普通的自定义实体类&#x1f34a;5.写个测试类验证一下&#x1f34a;6.控制台打印…

竞赛 目标检测-行人车辆检测流量计数

文章目录 前言1\. 目标检测概况1.1 什么是目标检测&#xff1f;1.2 发展阶段 2\. 行人检测2.1 行人检测简介2.2 行人检测技术难点2.3 行人检测实现效果2.4 关键代码-训练过程 最后 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 行人车辆目标检测计数系统 …

Linux之make/maakefile

access不是实时更新的。 printf打印并不是直接给屏幕而是先放到缓冲区。 可以通过fflush(stdout)强制刷新缓冲区。 换行是指直接到同一位置的下一行&#xff0c;回车是回到开头。

【MySQL数据库】 七

本文主要介绍了Java的JDBC编程的过程. 超详细 !!! 一.JDBC JDBC就是通过Java代码,来操作数据库 由于我们在实际开发中,绝大部分都是用代码来操作数据库的 , 因此一个成熟的数据库,都会提供一些API让程序员来使用. 常见的数据库比如mysql / oracl / sqlserver / SQLite 都会提…

Python类与对象:类的定义、实例化、方法、属性、构造函数

文章目录 类的定义类的实例化方法属性构造函数Python 类和对象是面向对象编程的基础。在 Python 中,几乎所有东西都是对象,拥有属性和方法。类是创建对象的蓝图或模板。让我们一步步来探索类的定义、实例化、方法、属性以及构造函数,并提供详细的代码示例。 类的定义 在 P…

SQL注入漏洞 其他注入

文章目录 宽字节注入案例 HTTP头部注入Cookie注入base64User-Agent注入Referer 注入 SQL注入读写文件条件1.是否拥有读写权限2.文件路径3.secure_file_priv 读取文件写入文件 SQLMap安装sqlmapkail 源安装仓库克隆 参数简介快速入门&#xff1b;SQLmap&#xff08;常规&#xf…

【Docker】设置容器系统字符集zh_CN.UTF-8退出失效:关于Docker容器配置环境变量,再次进入失效问题

设置容器系统字符集zh_CN.UTF-8退出失效&#xff1a;关于Docker容器配置环境变量&#xff0c;再次进入失效问题 修改正在运行的Docker容器内的字符集: 先进入Docker容器&#xff1a;docker exec -it 容器ID /bin/bash查看是否支持中文字符集&#xff1a;locale -a | grep zh&a…

B站双11,联手天猫暴涨2亿消费新势力

一直以来&#xff0c;手持高活跃、高粘性用户群体的B站是行业用来观察年轻人消费习惯的重要平台。以至于用户群体的不断壮大带动了B站的商业价值。如今B站的商业舞台越来越大&#xff0c;不断地向外界招手&#xff0c;欢迎更多品牌积极加入到这个千万年轻人聚集的内容社区。 2…

如何有效防爬虫?教你打造安全堡垒

企业拥抱数字化技术的过程中&#xff0c;网络犯罪分子的“战术”也更难以觉察&#xff0c;并且这些攻击越来越自动化和复杂&#xff0c;也更加难以觉察。在众多攻击手段总&#xff0c;网络爬虫是企业面临的主要安全挑战&#xff0c;对于企业所造成的经济损失是难以计量的。那么…

设备密集型单位如何提升效率?智能巡检软件哪个好?

在设备密集型单位&#xff0c;如钢铁、化工、电力、烟草、日用品等行业以及运维商&#xff0c;日常工作中面临着设备巡检这一重要且繁琐的任务。传统的巡检方式往往依靠纸质记录&#xff0c;数据难以进行统计或分析&#xff0c;巡检人员需要手动记录各种数据&#xff0c;不仅效…

visual studio 启用DPI识别功能

在开发widow程序时&#xff0c;有时必须将电脑 设置-->显示-->缩放与布局-->更改文本、应用项目的大小-->100%后&#xff0c;程序的画面才能正确运行&#xff0c;居说这是锁定了dpi的原因&#xff0c;需要启dpi识别功能。设置方法如下&#xff1a; 或者

unity - Blend Shape - 变形器 - 实践

文章目录 目的Blend Shape 逐顶点 多个混合思路Blender3Ds maxUnity 中使用Project 目的 拾遗&#xff0c;备份 Blend Shape 逐顶点 多个混合思路 blend shape 基于&#xff1a; vertex number, vertex sn 相同&#xff0c;才能正常混合、播放 也就是 vertex buffer 的顶点数…

CocosCreator让一个物体跟随鼠标移动(两种方式 本地坐标系和世界坐标系)

在 Cocos Creator 3.x 游戏运行时显示的画布大小就是屏幕区域&#xff0c;屏幕坐标是从画布的左下角为原点开始计算 在 Creator 3.x 里&#xff0c;屏幕和 UI 是完全区分开的&#xff0c;用户可以在没有 UI 的情况下点击屏幕获取触点信息。因此&#xff0c;获取屏幕触点&#…

Jmeter工具二次开发

一、JMeter 二次开发方向 1、函数开发&#xff0c;主要为JMeter 函数库 2、插件开发&#xff0c;一般主要做取样器开发 3、基于执行引擎开发&#xff0c;有效解决单独开发的测试平台或工具中&#xff0c;底层执行引擎开发相对复杂、周期长的问题&#xff0c;利用 JMeter 执行…

分享vmware和Oracle VM VirtualBox虚拟机的区别,简述哪一个更适合我?

VMware和Oracle VM VirtualBox虚拟机的区别主要体现在以下几个方面&#xff1a; 首先两种软件的安装使用教程如下&#xff1a; 1&#xff1a;VMware ESXI 安装使用教程 2&#xff1a;Oracle VM VirtualBox安装使用教程 商业模式&#xff1a;VMware是一家商业公司&#xff0c;而…

数据结构: 哈希桶

目录 1.概念 2.模拟实现 2.1框架 2.2哈希桶结构 2.3相关功能 Modify --Insert --Erase --Find 2.4非整型数据入哈希桶 1.仿函数 2.BKDR哈希 1.概念 具有相同地址的key值归于同一集合中,这个集合称为一个桶,各个桶的元素通过单链表链接 2.模拟实现 2.1框架 a.写出…