redis中分布式锁的应用

我们之前讲了秒杀模块的实现,使用了sychronized互斥锁,但是在集群模式下因为不同服务器有不同jvm,所以synchronized互斥锁失效了。

redis实现秒杀超卖问题的解决方案:(仅限于单体项目)-CSDN博客

这时就要找到一个多台服务器都能识别的锁,即redis中的setNX充当互斥锁,来控制秒杀的一人一单

在redis缓存击穿中,使用逻辑过期就用过互斥锁,这里原理一摸一样,只不过这里存储的value为UUID+线程ID

setNX互斥锁的使用:

场景1:(会导致一个用户创建多个订单)

注*线程1和线程2的userID相同,所以创建的redis锁key值相同,但是value不相同,释放锁时如果不进行验证value值,很有可能会出现场景1的情况。

场景2:

注*在线程1检查锁后,发现自己的锁过期了,该锁不是自己创建的,说明其他相同userID的线程也在创建订单,这时应该回滚,撤销之前数据库操作。(在调用减库存创建订单的方法中回滚)

代码实现:

锁工具:

创建后不能注册为Bean,用的时候new对象即可,如果想注册为Bean使用keywords和value都要作为参数传递,否者会出现多线程随意修改该值的情况

public class RedisLock {StringRedisTemplate template;String keywords;String value;public RedisLock(StringRedisTemplate template,String keywords){this.keywords=keywords;this.template=template;}//尝试创建锁public boolean tryLock(Integer timeOutSecond){//给该线程生成唯一标识,作为valuevalue=UUID.randomUUID().toString().replace("-","")+"-"+Thread.currentThread().getId();return template.opsForValue().setIfAbsent("lock:"+keywords,value,timeOutSecond, TimeUnit.SECONDS);}//尝试删除锁public void delLovk(){//释放锁之前先验证锁是否过期,是为自己的锁String result = template.opsForValue().get("lock:" + keywords);if(result!=null || result.equals(value)){template.delete("lock:"+keywords);}}
}

之所以不将该类注册为bean使用,是因为创建锁时,要获取UUID+线程的ID,删除锁时也需要该值,所以这个值只能使用一个全局变量来记录。

如果注册为bean后,所有线程的操作都使用该对象中value属性去进行赋值和删除操作,就会导致value被不断修改,keywords也会被不断修改,最终导致程序逻辑错误,应该一个线程使用一个独有的value属性,所以不能将该工具类注册为Bean,用的时候new即可

业务逻辑代码:有原来的synchronized改为分布式锁控制线程创建订单

@AutowiredApplicationContext context;//模仿秒杀减库存,创建订单@Overridepublic Boolean killInSecond(Integer userID,Integer productID){//检查库存是否>0Product product = pm.selectByPrimaryKey(productID);if(product.getSales()<=0){throw new MyExceptionHandler("库存不足");}//调用2-4步骤方法Boolean result=false;//同步锁
//        synchronized (userID.toString().intern()){
//            //使用代理对象调用事务方法
//            ProductServiceImpl bean = context.getBean(ProductServiceImpl.class);
//            result=bean.ProductAndOrder(userID,productID);
//        }//分布式锁RedisLock redisLock = new RedisLock(template, "order:" + userID);//获取锁result=redisLock.tryLock(30);if(!result){throw new RuntimeException("该用户只能创建一个订单");}//使用代理对象调用事务方法ProductServiceImpl bean = context.getBean(ProductServiceImpl.class);result=bean.ProductAndOrder(userID,productID);//释放锁redisLock.delLovk();return result;}

减库存创建订单方法:

 @AutowiredOrderMapper om;@AutowiredRedisIdIncrement redisId;//创建订单,减库存操作@Transactionalpublic Boolean ProductAndOrder(Integer userID,Integer productID){//检查数据库中书否存在该用户订单Integer orderCount = om.selectOrderByUserIdAndProductId(userID, productID);if(orderCount>0){throw new MyExceptionHandler("用户已下单");}//订单不存在减库存,宽松乐观锁Integer result = pm.updateProductBysale(productID);if(result!=1){throw new MyExceptionHandler("库存不足");}//创建订单//获取redis唯一IDLong orderId = redisId.getRedisID("order");//封装订单Order order=new Order(orderId.toString(),userID,"","",productID,"",null,1,0,null,null,null,null,new BigDecimal(100));result = om.insertCompleteOrder(order);if(result!=1){return false;}return true;}

不足:

虽然我们通过检查锁的value值判断该锁是否为本线程创建的锁,控制了误删锁的可能,但是这里依然会没有解决多个相同userID的线程,会创建多个订单的情况。

情况一:

在线程1检查锁后,发现自己的锁过期了,该锁不是自己创建的,说明其他相同userID的线程也在创建订单,这时应该回滚,撤销之前数据库操作。(在调用减库存创建订单的方法中回滚)

情况2:

删除锁时发现自己的锁过期了,缓存中没有该锁,说明

1.没有其他相同userID用户执行创建订单的逻辑,不需回滚直接结束程序即可

2.有其他线程执行了操作,但是已经执行完毕,订单也已经创建完毕,继续执行程序即可,因为创建订单时发现订单已存在,自会回滚

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

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

相关文章

【科研绘图系列】R语言绘制微生物丰度和基因表达值的相关性网络图

文章目录 介绍 加载R包 数据下载 导入数据 数据预处理 画图 系统信息 参考 介绍 【科研绘图系列】R语言绘制微生物丰度和基因表达值的相关性网络图 加载R包 library(tidyverse) library(ggsignif) library(RColorBrewer) library(dplyr) library(reshape2) library(grid

Pycharm现有conda环境有对应env,但是添加后没反应

一、系统环境 二、异常现象 Pycharm现有conda环境有对应env&#xff1a; anaconda3的envs下也确实存在这个环境&#xff1a; 但是添加后没反应&#xff08;点击确认后&#xff0c;yolov7环境没有出现在列表中&#xff09;&#xff1a; 但是我之前在别的机子添加是没问题的。 …

Git常用指令大全:从入门到精通

Git 的常用指令&#xff0c;分为基础操作、分支管理、远程协作、撤销操作和高级功能五个部分&#xff0c;并附上实用示例&#xff1a;一、基础操作&#xff08;必会&#xff09;初始化仓库 git init # 在当前目录创建新仓库克隆远程仓库 git clone https://github.com/user/rep…

Redis (REmote DIctionary Server) 高性能数据库

Redis {REmote DIctionary Server} 高性能数据库1. What is Redis?1.1. 基于内存的数据存储2. Install Redis on Linux3. Starting and stopping Redis in the background3.1. systemctl3.2. service 4. Connect to Redis5. 退出 Redis 的命令行界面 (redis-cli)6. redis-serv…

MySQL中的DML(二)

DML(Data Manipulation Language) : 数据库操作语言&#xff0c;对数据库中表的数据进行增删改操作。 创建student表&#xff1a; CREATE DATABASE test; use test; CREATE TABLE student (id int,name varchar(255),address varchar(255),city varchar(255) );INSERT INTO stu…

linux 主机驱动(SPI)与外设驱动分离的设计思想

一、 主机驱动与外设驱动分离Linux中的SPI、I2c、USB等子系统都利用了典型的把主机驱动和外设驱动分离的想法&#xff0c;让主机端负责产生总线上的传输波形&#xff0c;而外设端只是通过标准的API来让主机端以适当的波形访问自身。因此这里涉及了4个软件模块&#xff1…

如何生成.patch?

文章目录 ​​方法 1:使用 `git format-patch`(推荐)​ ​​步骤​​ ​方法 2:使用 `diff`命令(适用于非 Git 项目)​ ​​方法 3:使用 `git diff`(生成未提交的变更)​ ​方法 4:使用 `quilt`(适用于大量补丁管理) ​如何提交补丁给上游项目?​ ​总结​​ 在 L…

【计算机网络 | 第6篇】计算机体系结构与参考模型

文章目录计算机体系结构与参考模型分层思想&#x1f342;常见的3种模型&#xff08;网络体系结构&#xff09;&#x1f426;‍&#x1f525;TCP/IP体系结构各层包含的主要协议&#x1f95d;每层所解决的主要问题&#x1f914;层次间的交互规则&#x1f95d;实体与对等实体协议服…

Autoware Universe 感知模块详解 | 第一节 感性认识多源传感器标定

传感器与感知模块 在基于规则的自动驾驶系统中&#xff0c;感知模块&#xff0c;承担着理解车体周围环境信息的重要职责。它通过融合多种传感器数据&#xff0c;与定位模块共同为规划与控制模块提供准确、系统化的输入信息。正如人可以通过眼睛观察周围的环境&#xff08;盲人也…

docker搭建java运行环境(java或者springboot)

目录1. 创建测试代码2. 编译打包3. 代码环境运行使用普通运行方式使用docker挂载项目&#xff08;长期运行&#xff09;1. 创建 Dockerfile2. 构建并后台运行使用docker swram实现零停机更新&#xff08;推荐&#xff09;1. 初始化swarm2. 创建 Dockerfile3. 使用Dockerfile 构…

哈希表特性与unordered_map/unordered_set实现分析

目录 一、哈希表核心特性总结 1.开放地址法 2.链地址法 二、unordered_map/unordered_set实现要点分析 1. 哈希表核心实现(HashTable2.h) (1) 哈希函数处理 (2) 链地址法实现 (3) 迭代器设计 (4) hashtable设计 2. unordered_map实现要点 3. unordered_map实现要点 一…

生产环境sudo配置详细指南

目录 1. 语法格式 2. 配置示例 3. 使用 /etc/sudoers.d/ 目录管理&#xff08;推荐&#xff09; 4. 基础配置&#xff1a;用户权限管理 4.1 ​​添加用户到sudo组 ​​4.2 验证用户组信息 5. sudo日志配置 5.1 修改sudoers配置文件 5.2 创建日志目录与权限设置 6. Su…

CSS动态视口单位:彻底解决移动端适配顽疾,告别布局跳动

你是否曾被这些问题困扰&#xff1a; 移动端页面滚动时&#xff0c;地址栏收缩导致页面高度突变&#xff0c;元素错位&#xff1f;100vh在移动设备上实际高度超出可视区域&#xff1f;全屏弹窗底部总被浏览器UI遮挡&#xff1f; 这些痛点背后都是传统视口单位的局限——无法响应…

【P27 4-8】OpenCV Python——Mat类、深拷贝(clone、copyTo、copy)、浅拷贝,原理讲解与示例代码

P27 4-8 1 Mat结构体2 深拷贝VS浅拷贝3 代码示例1 Mat结构体 2 深拷贝VS浅拷贝 只拷贝了头部&#xff0c;header&#xff0c;&#xff0c;但是data部分是共用的&#xff0c;速度非常快&#xff1b; 缺点&#xff0c;任意一个修改&#xff0c;另一个data跟着变&#xff0c;这就是…

容器运行时支持GPU,并使用1panel安装ollama

前言 安装Docker请看之前博文&#xff1a;Docker实战中1panel方式安装Docker。 安装 NVIDIA 容器工具包 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 安装 先决条件 阅读有关平台支持的部分。为您的 Linux 发行版安装…

高并发内存池 性能瓶颈分析与基数树优化(9)

文章目录前言一、性能瓶颈分析操作步骤及其环境配置分析性能瓶颈二、基数树优化单层基数树二层基数树三层基数树三、使用基数树来优化代码总结前言 到了最后一篇喽&#xff0c;嘻嘻&#xff01;   终于是要告一段落了&#xff0c;接下来我们将学什么呢&#xff0c;再说吧&…

C#面试题及详细答案120道(01-10)-- 基础语法与数据类型

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

机器翻译:回译与低资源优化详解

文章目录一、机器翻译的瓶颈二、回译&#xff08;Back-Translation&#xff09;2.1 什么是回译&#xff1f;2.2 为什么回译有效&#xff1f;2.3 回译的缺点与挑战三、低资源优化详解3.1 数据层面策略3.2 模型层面策略3.3 架构层面策略四、回译与低资源优化对比4.1 回译与低资源…

leetcode-python-344反转字符串

题目&#xff1a; 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1a;s [“h”,“…

【Python】新手入门:什么是python字符编码?python标识符?什么是pyhon保留字?

🌈 个人主页:(时光煮雨) 🔥 高质量专栏:vulnhub靶机渗透测试 👈 希望得到您的订阅和支持~ 💡 创作高质量博文(平均质量分95+),分享更多关于网络安全、Python领域的优质内容!(希望得到您的关注~) 🌵文章目录🌵 前言 💡一、编码 📝二、标识符 🎯三、Py…