2、基于redis实现分布式锁

news2024/10/7 12:18:40

目录

    • 2.1. 基本实现
    • ==2.2. 防死锁==
    • ==2.3. 防误删==
    • 2.4. redis中的lua脚本
      • 2.4.1 redis 并不能保证
      • 2.4.2 lua介绍
    • 2.5. 使用lua保证删除原子性

2.1. 基本实现

借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。
在这里插入图片描述

  1. 多个客户端同时获取锁(setnx)
  2. 获取成功,执行业务逻辑,执行完成释放锁(del)
  3. 其他客户端等待重试

改造StockService方法:

 /**
     * redis setnx实现分布式锁,最基本的哪一种 !!!
     */
    public void deduct() {
        // 加锁setnx
        Boolean lock = this.redisTemplate.opsForValue().setIfAbsent("lock", "111");
        if (!lock) {
            // 没有获取到锁,进行重试!!
            try {
                Thread.sleep(50);
                this.deduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            try {
                // 1. 查询库存信息
                String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
                // 2. 判断库存是否充足
                if (stockStr != null && stockStr.length() != 0) {
                    Long stock = Long.parseLong(stockStr);
                    if (stock > 0) {
                        redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                    }
                }
            } finally {
                // 解锁
                this.redisTemplate.delete("lock");
            }
        }
    }

使用 jmeter 进行压测
在这里插入图片描述
查看库存数量
在这里插入图片描述

上述代码优化,不断重试的过程中一直进行递归,最终导致栈的溢出。

解决


    /**
     *  while循环代替递归,解决不断重试可能导致的栈溢出的问题
     */
    public void deduct() {
        // 加锁setnx
        while (this.redisTemplate.opsForValue().setIfAbsent("lock1", "1")) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                // 1. 查询库存信息
                String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
                // 2. 判断库存是否充足
                if (stockStr != null && stockStr.length() != 0) {
                    Long stock = Long.parseLong(stockStr);
                    if (stock > 0) {
                        redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                    }
                }
            } finally {
                // 解锁
                this.redisTemplate.delete("lock1");
            }
        }
    }

2.2. 防死锁

问题:setnx刚刚获取到锁,当前服务器宕机,导致del释放锁无法执行,进而导致锁无法锁无法释放(死锁)
解决:给锁设置过期时间,自动释放锁。

设置过期时间两种方式:

  • 通过expire设置过期时间(缺乏原子性:如果在setnx和expire之间出现异常,锁也无法释放)
  • 使用set指令设置过期时间:set key value ex 3 nx(既达到setnx的效果,又设置了过期时间)

在这里插入图片描述

2.3. 防误删

持有锁的线程在锁的内部出现了阻塞,导致他的锁自动释放,这时其他线程,线程2来尝试获得锁,就拿到了这把锁,然后线程2在持有锁执行过程中,线程1反应过来,继续执行,而线程1执行过程中,走到了删除锁逻辑,此时就会把本应该属于线程2的锁进行删除,这就是误删别人锁的情况说明

解决: setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的锁
在这里插入图片描述

问题:删除操作缺乏原子性。
场景:

  1. index1执行删除时,查询到的lock值确实和uuid相等
  2. index1执行删除前,lock刚好过期时间已到,被redis自动释放
  3. index2获取了lock
  4. index1执行删除,此时会把index2的lock删除

解决方案:没有一个命令可以同时做到判断 + 删除,所有只能通过其他方式实现(LUA脚本

2.4. redis中的lua脚本

2.4.1 redis 并不能保证

redis采用单线程架构,可以保证单个命令的原子性,但是无法保证一组命令在高并发场景下的原子性

如果redis客户端通过lua脚本把3个命令一次性发送给redis服务器,那么这三个指令就不会被其他客户端指令打断。Redis 也保证脚本会以原子性的方式执行: 当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。 这和使用 MULTI/ EXEC 包围的事务很类似。

2.4.2 lua介绍

2.5. 使用lua保证删除原子性

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

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

相关文章

PhonewindowManager 使用详解

和你一起终身学习,这里是程序员Android 经典好文推荐,通过阅读本文,您将收获以下知识点: 一、Android 按键修改二、PhoneWindowManager 简介三、如何打开 或者 关闭 Navigation Bar四、如何长按Home 键启动Google Now五、如何长按实体Menu键进…

B. Vika and the Bridge

Example input 5 5 2 1 1 2 1 1 7 3 1 2 3 3 3 2 1 6 6 1 2 3 4 5 6 8 4 1 2 3 4 2 3 1 4 3 1 1 1 1 output 0 1 2 2 0 解析: 题意为每次只能踩相同颜色的木板,同时他有一次改变一块木板颜色的机会,问每种颜色的最大跨的步子长度中的最小…

Linux复习——基础知识

作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​ 1. 有关早期linux系统中 sysvin的init的7个级别描述正确的是( )[选择1项] A. init 1 关机状态 B. init 2 字符界面多用户模式 …

科技云报道:大模型的火烧到了AI服务器上

科技云报道原创。 大模型的纷争已经随着各大入局者公布产品后,热度逐渐退去,但是由大模型带来的产业链高频共振,已经传递了算力层。 表现最为激烈的,就是AI服务器市场。大模型带来的算力需求,直接引发了一波AI服务器…

算法(4)

字符串 给定一个数值的数组&#xff0c;要求组合最小的数值。 public String PrintMinNumber(Integer [] s) {if(snull) return null;String s1"";ArrayList<Integer> listnew ArrayList<Integer>(Arrays.asList(s)); // for(int i0;i<s.leng…

二维数组练习题-回形数

从键盘输入一个整数&#xff08;1~20&#xff09;&#xff0c;则以该数字为矩阵&#xff0c;将数字按照顺时针螺旋填入其中 package array;import java.util.Scanner;/*** author 苗晓强* date 2023/7/26 23:56* 回形数&#xff1a;* 从键盘输入一个整数&#xff08;1~20&…

从9G到0.3G,腾讯会议对他们的git库做了什么?

&#x1f449;导读 过去三年在线会议需求井喷&#xff0c;腾讯会议用户量骤增到3亿。快速迭代的背后&#xff0c;腾讯会议团队发现&#xff1a;业务保留了长达5年的历史数据&#xff0c;大量未进行 lfs 转换&#xff0c;新 clone 仓库本地空间占17.7G。本地磁盘面临严重告急&am…

FastSAM 论文解读

论文名称&#xff1a;Fast Segment Anything 论文地址&#xff1a;http://export.arxiv.org/pdf/2306.12156 代码地址&#xff1a;GitHub - CASIA-IVA-Lab/FastSAM: Fast Segment Anything 1. 关键内容 基于YOLOv8-seg实现了FastSAM&#xff0c;它比SAM快50倍&#xff0c;且…

数仓学习---15、数据仓库工作流调度

1、数据仓库工作流调度 1.1 调度工具部署 工具部署链接 1.2 新数据生成 1.2.1 用户行为日志 1、启动日志采集通道&#xff0c;包括Kafka、Flume等 &#xff08;1&#xff09;启动Zookeeper zk.sh start&#xff08;2&#xff09;启动Kafka kf.sh start&#xff08;3&…

【雕爷学编程】Arduino动手做(95)---GY9960手势传感器模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

电容触摸屏(TP)的工艺结构

液晶显示屏(LCM),触摸屏(TP) “GG、GP、GF”这是结构分类&#xff0c;第一个字母表面材质&#xff08;又称为上层&#xff09;&#xff0c;第二个字母是触摸屏的材质&#xff08;又称为下层&#xff09;&#xff0c;两者贴合在一起。 G玻璃&#xff0c;FFILM&#xff0c;“”贴…

华为eNSP:路由引入

一、拓扑图 二、路由器的配置 1、配置路由器的IP AR1&#xff1a; [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [Huawei-GigabitEthernet0/0/0]qu AR2&#xff1a; [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 1.1.1.2 24 [Huaw…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 一)

状态管理概述 在前文的描述中&#xff0c;我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面&#xff0c;就需要引入“状态”的概念。 图1 效果图 上面的示例中&#xff0c;用户与应用程序的交互触发了文本状态变更&#xff0c;状态变更引起了UI渲染&#x…

Blazor实战——Known框架多表增删改查

多表增删改查示例 本章介绍学习多张表增、删、改、查功能如何实现&#xff0c;下面以销货出库单作为示例&#xff0c;该业务栏位如下&#xff1a; 销货出库单栏位 销货单号、销货日期、状态、客户、备注 销货出库单明细栏位 商品编码、商品名称、规格型号、数量、单位、单价、…

详解rocketMq通信模块升级构想

本文从开发者的角度深入解析了基于netty的通信模块, 并通过简易扩展实现微服务化通信工具雏形, 适合于想要了解netty通信框架的使用案例, 想了解中间件通信模块设计, 以及微服务通信底层架构的同学。希望此文能给大家带来通信模块架构灵感。 概述 网络通信是很常见的需求&#…

065、故障处理之OMM_TiKV

TiKV Server OOM 对业务的影响 TiKV 上的请求失败造成异常退出region leader重新选举 raft group 开始选举新的 region leader新的region leader 上报信息给PD Server region cache频繁更新 在访问TiDB Server的region cache时&#xff0c;出现TiKV rpc相关报错后台自动进行Ba…

解放程序员,加速创新,缺少的就是一个工具而已

随着科技的不断进步和应用场景的不断扩大&#xff0c;软件开发已经成为当今世界的核心驱动力之一。 然而&#xff0c;传统的软件开发模式往往存在着繁琐的编码过程、复杂的架构设计和漫长的调试周期&#xff0c;使得程序员们难以专注于创新和高难度的研究。 很多程序员上班的时…

基于解析法和遗传算法相结合的配电网多台分布式电源降损配置(Matlab实现)

目录 1 概述 2 数学模型 2.1 问题表述 2.2 DG的最佳位置和容量&#xff08;解析法&#xff09; 2.3 使用 GA 进行最佳功率因数确定和 DG 分配 3 仿真结果与讨论 3.1 33 节点测试配电系统的仿真 3.2 69 节点测试配电系统仿真 4 结论 1 概述 为了使系统网损达到最低值&a…

一分钟学会利用GPT编写爆款标题

&#x1f3c6; 文章目标&#xff1a;学习利用GPT编写爆款标题 &#x1f340; 入门篇&#xff1a;一分钟学会利用GPT编写爆款标题 ✅ 创作者&#xff1a;熊猫Jay ✨ 个人公众号: 熊猫Jay字节之旅 (文末有链接) &#x1f341; 展望&#xff1a;若本篇讲解内容帮助到您&#xff0c…

Web网站性能压测实践 | 数据平台

一、 为什么要做压测&#xff1f; 首先解释下为什么要做性能压测&#xff1a;根据 Amazon 统计&#xff0c;每慢 100 毫秒&#xff0c;交易额下降 1%。这个统计数据为大家敲响了警钟&#xff0c;也客观说明了性能压测对于企业应用的重要性。从具体的OKR上讲&#xff0c;我们希望…