分布式锁优化之 使用lua脚本改造分布式锁保证判断和删除的原子性(优化之LUA脚本保证删除的原子性)

news2024/11/15 11:17:37

文章目录

  • 1、lua脚本入门
    • 1.1、变量:弱类型
    • 1.2、流程控制
    • 1.3、在lua中执行redis指令
    • 1.4、实战:先判断是否自己的锁,如果是才能删除
  • 2、AlbumInfoApiController --》testLock()
  • 3、AlbumInfoServiceImpl --》testLock()

1、lua脚本入门

Lua 教程 | 菜鸟教程:https://www.runoob.com/lua/lua-tutorial.html\

redis提供了一组命令行直接可以执行lua脚本。
redis中的lua脚本是有原子性:因为是通过一个指令执行的

[root@localhost ~]# docker exec -it spzx-redis redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> eval script numkeys key [key ...] arg [arg ...]

script:lua 脚本字符串
numkeys:需要传递的key列表的元素数量
key:指定具体key列表,元素之间用空格区分,用KEYS[Iindex从1开始]
arg:指定具体arg列表,元素之间用空格区分,用ARGV[index从1开始]

127.0.0.1:6379> eval "return 'hello world!'" 0
"hello world!"

1.1、变量:弱类型

全局变量:a = 5
局部变量:local a = 5   redis中的lua脚本只支持局部变量
127.0.0.1:6379> eval "local a=5 return a" 0
(integer) 5

1.2、流程控制

127.0.0.1:6379> eval "if 10>20 then return 10 else return 20 end" 0
(integer) 20

要修正你的脚本,你应该使用 KEYS 和 ARGV 而不是 keys 和 argv(注意大小写)。KEYS 数组包含了传递给脚本的键名(key names),而 ARGV 数组包含了传递给脚本的参数(arguments)。

127.0.0.1:6379> eval "if keys[1]>argv[1] then return keys[2] else return argv[2] end" 2 10 20 30 40
(error) ERR Error running script (call to f_6913b8a1459b5bd1de1628cd76db221048464f0a): @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'keys'
127.0.0.1:6379> eval "if KEYS[1]>ARGV[1] then return KEYS[2] else return ARGV[2] end" 2 10 20 30 40
"40"
127.0.0.1:6379> eval "return {10,20,30,40}" 0
1) (integer) 10
2) (integer) 20
3) (integer) 30
4) (integer) 40
127.0.0.1:6379> eval "return {KEYS[1],KEYS[3],ARGV[1],ARGV[3]}" 4 10 20 30 40 50 60 70 80 90 
1) "10"
2) "30"
3) "50"
4) "70"

1.3、在lua中执行redis指令

redis.call(‘指令名称’,…)

127.0.0.1:6379> eval "return redis.call('set','lock','333')" 0
OK
127.0.0.1:6379> get lock
"333"
127.0.0.1:6379> eval "return redis.call('get','lock')" 0
"333"
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 lock 123
OK
127.0.0.1:6379> get lock
"123"

1.4、实战:先判断是否自己的锁,如果是才能删除

eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"
eval "if redis.call('get',KEYS[1]) == ARGV[1] 
then 
	return redis.call('del',KEYS[1]) 
else 
	return 0 
end"
127.0.0.1:6379> eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock 4343323
(integer) 0

在这里插入图片描述

127.0.0.1:6379> eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock 123
(integer) 1

在这里插入图片描述

2、AlbumInfoApiController --》testLock()

@Tag(name = "专辑管理")
@RestController
@RequestMapping("api/album/albumInfo")
@SuppressWarnings({"unchecked", "rawtypes"})
public class AlbumInfoApiController {

	@GetMapping("test/lock")
	public Result testLock() {
		this.albumInfoService.testLock();
		return Result.ok("测试分布式锁案例");
	}
	
}

3、AlbumInfoServiceImpl --》testLock()

    @Override
    public  void testLock(){
        // 加锁:set k v nx ex 3
        String uuid = UUID.randomUUID().toString();
        Boolean lock = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
        if (!lock) {
            try {
                // 获取锁失败,进行自旋
                Thread.sleep(50);
                this.testLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }else {
            // 获取锁成功,执行业务
            //this.redisTemplate.expire("lock", 3, TimeUnit.SECONDS);

            Object numObj = this.redisTemplate.opsForValue().get("num");
            if (numObj == null) {
                this.redisTemplate.opsForValue().set("num", 1);
                return;
            }
            Integer num = Integer.parseInt(numObj.toString());
            this.redisTemplate.opsForValue().set("num", ++num);

            // 解锁
            // 先判断是否是自己的锁,如果是则删除
            //if (StringUtils.equals(uuid, this.redisTemplate.opsForValue().get("lock").toString())) {
            //    this.redisTemplate.delete("lock");
            //}
            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);
        }
    }

在这里插入图片描述
压力测试肯定也没有问题。自行测试
启动多个运行实例:
在这里插入图片描述
在这里插入图片描述
redis中的值重新改为0。

[root@localhost ~]# ab -n 5000 -c 100 http://192.168.74.1:8500/api/album/albumInfo/test/lock
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.74.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests


Server Software:        
Server Hostname:        192.168.74.1
Server Port:            8500

Document Path:          /api/album/albumInfo/test/lock
Document Length:        76 bytes

Concurrency Level:      100
Time taken for tests:   49.543 seconds
Complete requests:      5000
Failed requests:        674
   (Connect: 0, Receive: 0, Length: 674, Exceptions: 0)
Write errors:           0
Total transferred:      2353370 bytes
HTML transferred:       383370 bytes
Requests per second:    100.92 [#/sec] (mean)
Time per request:       990.856 [ms] (mean)
Time per request:       9.909 [ms] (mean, across all concurrent requests)
Transfer rate:          46.39 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   2.6      1      81
Processing:     5  948 2587.5     14   33884
Waiting:        5  948 2587.5     14   33883
Total:          6  950 2587.7     16   33887

Percentage of the requests served within a certain time (ms)
  50%     16
  66%    197
  75%    627
  80%   1054
  90%   2846
  95%   5011
  98%   8277
  99%  12678
 100%  33887 (longest request)

在这里插入图片描述

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

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

相关文章

STM32F407单片机编程入门(十四) 内部RTC实时时钟详解及实战含源码

文章目录 一.概要二.RTC基本介绍三.STM32单片机RTC内部结构图四.CubeMX配置一个RTC时间例程五.CubeMX工程源代码下载六.小结 一.概要 RTC&#xff08;Real-Time Clock&#xff09;是一种用于追踪和记录实际时间的时钟系统。在STM32中&#xff0c;RTC通常用于提供实时时钟和日期…

【python】修改字典的内容

person {"name": "John", "age": 30, "city": "New York"} print("最开始的信息&#xff1a;",person)def process_person_info(person):# 检查对象中是否包含所有必要的键if name in person and age in person …

MySQL缓冲池详解

Buffer Pool 本文参考开源项目&#xff1a;小林coding在线文档&#xff1b; 01-缓冲池概述 ​ 在MySQL查询数据的时候&#xff0c;是通过存储引擎去磁盘做IO来获取数据库中的数据&#xff0c;这样每次查询一条数据都要去做一次或者多次磁盘的IO&#xff0c;无疑是非常慢的。…

(C++23) expected 基础使用

文章目录 ⭐前言⭐expected&#x1f39b;️基础使用&#x1f39b;️单子操作 (Monadic operations)&#x1f39a;️and_then & or_else&#x1f39a;️transform & transform_error ⭐END&#x1f31f;跋&#x1f31f;交流方式 ⭐前言 在 C17 中&#xff0c;提出了 op…

量化交易backtrader实践(二)_基础加强篇(4)_交易设置与自定义绘图

这一节我们来深入实践交易的设置以及自定义绘图。 01_交易设置 参考文档&#xff1a; Backtrader系列教程④&#xff1a;交易篇&#xff08;上&#xff09;_backtrader撤单操作-CSDN博客量化投资之工具篇一&#xff1a;Backtrader从入门到精通&#xff08;8&#xff09;-交易…

记软件开发者画图(UML),使用WPS应用制图

目录 前言 一、什么是UML 二、使用什么画图工具 三、示例 ​四、IntelliJ IDEA 2021快速生成UML图 前言 做软件开发的从写第一个示例程序到最后写项目程序避不开的需要设计画图&#xff0c;所以今天我们就来梳理一下‌UML&#xff08;统一建模语言&#xff09;图形需要画…

《深度学习》卷积神经网络CNN 原理及其流程解析

目录 一、CNN图像原理 1、了解图像的原理 2、举例 二、CNN图像识别 1、画面不变性 2、主要表现 1&#xff09;平移不变性 2&#xff09;尺度不变性 3&#xff09;旋转不变性 3、传统神经网络识别 1&#xff09;数据预处理 2&#xff09;特征提取 3&#xff09;搭建神经网…

C++基础(8.继承_多态)

目录 继承&#xff1a; 继承的概念&#xff1a; 继承的定义&#xff1a; 基类和派生类对象赋值转换 &#xff1a; 继承中的作用域&#xff1a; 派生类的默认成员函数&#xff1a; 例题&#xff1a; ​编辑​编辑​编辑 继承与友元&#xff1a; 继承与静态成员&#xff1…

图片马赛克处理(Java)

1.需求 给图片的指定区域打码给整张图片打码马赛克方格取色支持中心点取色和随机取色马赛克支持灰度处理 2.源码 package com.visy.utils;import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOE…

Rabbitmq消息队列,安装,使用,三种工作模式

产品 消息队列技术介绍 消息队列概述 消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用耦合、异步消息、流量削锋等问题。实现高性能、高可用、可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。 目前在生产环境&#xff0c;使用较多的消息队列有…

基于51单片机的汽车倒车防撞报警器系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 本课题基于微控制器控制器&#xff0c; 设计一款汽车倒车防撞报警器系统。 要求&#xff1a; 要求&#xff1a;1.配有距离&#xff0c; 用于把车和障碍物之间的距离信号送入控制器。 2.配有报警系…

2024AI做PPT软件如何重塑演示文稿的创作

现在AI技术的发展已经可以帮我们写作、绘画&#xff0c;最近我发现了不少ai做ppt的工具&#xff01;不体验不知道&#xff0c;原来合理使用AI工具可以有效的帮我们进行一些办公文件的编写&#xff0c;提高了不少工作效率。如果你也有这方面的需求就接着往下看吧。 1.笔灵AIPPT…

内网穿透out了?黑群晖+IPv6+NAS公网助手的访问体验

科技宅最带折腾黑群晖&#xff0c;这不&#xff0c;尝试一下ipv6动态域名解析&#xff0c;远程访问群晖NAS的方法千千万&#xff0c;这个方法我早就想到了&#xff0c;今天终于体验了一把&#xff0c;把经验分享一下&#xff1a; 目录 黑群晖的魅力 IPv6的加入&#xff1a;无…

Python办公自动化教程(003):PDF的加密

【1】代码 from PyPDF2 import PdfReader, PdfWriter# 读取PDF文件 pdf_reader PdfReader(./file/Python教程_1.pdf) pdf_writer PdfWriter()# 对第1页进行加密 page pdf_reader.pages[0]pdf_writer.add_page(page) # 设置密码 pdf_writer.encrypt(3535)with open(./file/P…

上位机图像处理和嵌入式模块部署(linux小系统开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 和若干年前相比较&#xff0c;现在嵌入式linux开发要简单得多。稍微贵一点的有树莓派&#xff0c;国产的有各种水果派&#xff0c;基本上都可以按照…

苍穹外卖学习日志 -----20天项目从零到完结-----含软件下载,环境配置,框架学习,代码编写,报错处理,测试联调,每日总结,心路历程等等......

年份 2024 基础&#xff1a;Javase Javaweb 已完结 2024 8.25---9.14 20天 Day-01 8.25 今天开始学习已经晚了&#xff0c;网盘下载了一下文件&#xff0c;做了一些开始项目的准备工作。 本来其实打算用notepad来写学习日志的&#xff0c;但是那个传…

如何给bat文件替换好看的图标

最近遇到软件运行在Windows系统&#xff0c;通过bat文件启动&#xff0c;但是bat文件的图标不好看&#xff0c;而且作为启动快捷方式放桌面看上去跟其他软件不搭调&#xff0c;于是得给bat文件换个软件图标。 软件ico图标 Windows系统下使用.ico文件作为软件图标。另外favicon…

go libreoffice word 转pdf

一、main.go 关键代码 完整代码 package mainimport ("fmt""github.com/jmoiron/sqlx""github.com/tealeg/xlsx""log""os/exec""path/filepath" ) import _ "github.com/go-sql-driver/mysql"import &q…

Python练习宝典:Day 1 - 选择题 - 基础知识

目录 一、踏上Python之旅二、Python语言基础三、流程控制语句四、序列的应用 一、踏上Python之旅 1.想要输出 I Love Python,应该使用()函数。 A.printf() B.print() C.println() D.Print()2.Python安装成功的标志是在控制台(终端)输入python/python3后,命令提示符变为: A.&…

数业智能心大陆:职场倦怠的新解法

什么是职业倦怠&#xff1f; 在职场中&#xff0c;职业倦怠的表现形式丰富多样。从数业智能心大陆 AI 心理咨询平台的数据来看&#xff0c;职业倦怠呈现出多种状态。教师可能对教学不再满怀热情&#xff0c;精心备课也成为过去式&#xff1b;情绪上容易烦躁、易怒&#xff0c;在…