当前位置: 首页 > news >正文

Redis高并发分布式锁

目录

场景描述

订单扣减场景举例

代码调整1

代码调整2

代码调整3

redisson锁续命核心代码


场景描述

订单扣减场景举例
//首先在redis中set stock 300
@RequestMapping("/deduct_stock")
public String deductStock() {int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")if (stock > 0) {int realStock = stock - 1;stringRedisTemplate.opsForValue().set("stock", realStock + ""); //jedis.set(key,value)System.out.println("扣减成功,剩余库存:" + realStock);} else {System.out.println("扣减失败,库存不足");}return "end";}

       以上场景肯定会出现并发问题,当有多个用户同时进行库存扣减的时候,可能在获取stock数量的时候获取到相同的值,有可能会出现此时只有一件库存,但是三个用户下单,出现库存超卖问题。

代码调整1
    @RequestMapping("/deduct_stock")public String deductStock() {synchronized (this){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")if (stock > 0) {int realStock = stock - 1;stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)System.out.println("扣减成功,剩余库存:" + realStock);} else {System.out.println("扣减失败,库存不足");}return "end";}}

       通过增加synchronize锁可以解决并发问题,但是只对单机有效。如果多服务器之间也会出现并发超卖问题。

代码调整2
@RequestMapping("/deduct_stock")public String deductStock() {String lockKey = "lock:product_101";//通过redis的setnx命令来模拟一把分布式锁,并设置超时时间,该指令是原子操作Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "101", 30, TimeUnit.SECONDS); //jedis.setnx(k,v)//如果设置失败,result会返回false.如果成功走正常扣减订单逻辑。if (!result) {return "error_code";}try {int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")if (stock > 0) {int realStock = stock - 1;stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)System.out.println("扣减成功,剩余库存:" + realStock);} else {System.out.println("扣减失败,库存不足");}return "end";//如果业务代码中出现异常,也要保证锁的释放,也就是setnx的删除操作,避免死锁} finally {stringRedisTemplate.delete(lockKey);}}

        以上代码通过setnx的方式在并发量不高的时候,可能没有什么问题,如果并发量较高,某个线程获取到锁有执行业务代码超过了设置的超时时间,就会有并发问题发生了。假设线程1执行完成该方法用时15秒,执行到10秒的时候,因为超时时间将锁释放了,此时线程2获取到锁并执行业务逻辑,执行过程中,线程1执行完业务,并通过finally又释放了一次锁,可此时线程2不一定执行完。这种情况就会出现严重的并发问题。

代码调整3

       通过上述代码会发现,造成该问题的主要因素是超时时间的问题,为了解决该问题使用redisson锁续命来完善代码。

引入redisson依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.6.5</version></dependency>

在启动类中配置

@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic Redisson redisson() {// 单机模式Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson) Redisson.create(config);}}

订单接口调整

  @Autowiredprivate Redisson redisson;  @RequestMapping("/deduct_stock")public String deductStock() {String lockKey = "lock:product_101";//获取锁对象RLock redissonLock = redisson.getLock(lockKey);//加分布式锁redissonLock.lock();  //  .setIfAbsent(lockKey, "101", 30, TimeUnit.SECONDS);try {int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")if (stock > 0) {int realStock = stock - 1;stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)System.out.println("扣减成功,剩余库存:" + realStock);} else {System.out.println("扣减失败,库存不足");}} finally {//解锁redissonLock.unlock();}return "end";}

通过使用redisson会自动每隔10秒检查是否还持有锁,如果持有锁就延长锁的时间,默认延长30秒。

      

redisson锁续命核心代码
    private void scheduleExpirationRenewal(final long threadId) {if (!expirationRenewalMap.containsKey(this.getEntryName())) {Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {public void run(Timeout timeout) throws Exception {RFuture<Boolean> future = RedissonLock.this.commandExecutor.evalWriteAsync(RedissonLock.this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) " +"then redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; end; return 0;",Collections.singletonList(RedissonLock.this.getName()), new Object[]{RedissonLock.this.internalLockLeaseTime,RedissonLock.this.getLockName(threadId)});future.addListener(new FutureListener<Boolean>() {public void operationComplete(Future<Boolean> future) throws Exception {RedissonLock.expirationRenewalMap.remove(RedissonLock.this.getEntryName());if (!future.isSuccess()) {RedissonLock.log.error("Can't update lock " +RedissonLock.this.getName() + " expiration", future.cause());} else {if ((Boolean) future.getNow()) {RedissonLock.this.scheduleExpirationRenewal(threadId);}}}});}}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);if (expirationRenewalMap.putIfAbsent(this.getEntryName(), task) != null) {task.cancel();}}}

相关文章:

Redis高并发分布式锁

目录 场景描述 订单扣减场景举例 代码调整1 代码调整2 代码调整3 redisson锁续命核心代码 场景描述 订单扣减场景举例 //首先在redis中set stock 300 RequestMapping("/deduct_stock") public String deductStock() {int stock Integer.parseInt(stringRedi…...

组件开发遇到的问题(vue的问题)

组件的开发和引用 <template><div class"xx">组件</div> </template> <script>export default {name: viewMore,components: {},data() {return {}}} </script> <style scoped>可以定义组件都有的样式 </style> …...

vue3前端开发,感受一下组合式api和VUE2选项式的差异

vue3前端开发,感受一下组合式api和VUE2选项式的差异&#xff01;今天开始&#xff0c;正式开始&#xff0c;进入学习Vue3的内容。以后代码&#xff0c;案例分享&#xff0c;都会采用组合式api的模式为大家做展示。 今天是第一节&#xff0c;带大家感受一下&#xff0c;Vue3的组…...

电梯调度问题文献阅读

电梯调度问题文献阅读 文章目录 电梯调度问题文献阅读[toc]文献阅读1. 陈纪龙,孟洪兵,吴刚等.六层电梯模拟系统的研究与实现[J].伊犁师范学院学报(自然科学版),2014,8(01):57-62.2. 陈福兰,杜虹.浅析电梯并联运行的几种调度方法[J].装备制造技术,2012,(10):178-180190.3. 马春江…...

文心一言 VS 讯飞星火 VS chatgpt (183)-- 算法导论13.4 7题

七、用go语言&#xff0c;假设用 RB-INSERT 将一个结点 x 插入一棵红黑树&#xff0c;紧接着又用 RB-DELETE 将它从树中删除。结果的红黑树与初始的红黑树是否一样&#xff1f;证明你的答案。 文心一言&#xff1a; 首先&#xff0c;我们需要明确RB-INSERT和RB-DELETE的基本操…...

ChatGLM vs ChatGPT

所有的NLP大模型 都是transformer结构 1.Mask attention 的策略不同 2.训练任务目标不同 国内大模型nb公司&#xff1a;百度、清华智谱 一、主流大模型 粉色&#xff1a;Encoder-only。 绿色&#xff1a;Encoder-Decoder&#xff0c;尽头智谱ChatGLM。 蓝色&#xff1a;…...

leetcode热题100.三数之和

Problem: 15. 三数之和 文章目录 题目解题方法复杂度Code 题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元…...

GitLab服务器忘记root密码处理方式

GitLab服务器忘记root密码处理方式 文章目录 GitLab服务器忘记root密码处理方式1. Gitlab查看用户id号1. 通过api接口查询2. 在Linux终端里直接通过curl命令查询 2. 进入GitLab数据库中查询并修改root密码 1. Gitlab查看用户id号 1. 通过api接口查询 接口查询地址&#xff1a;…...

js-cookie的使用--token的数据实现持久化

1.下载 npm install js-cookie 2.引入 import Cookies from "js-cookie"; 3.使用 // 写入cookie Cookies.set(name, value) // 读取 Cookies.get(name) // > value Cookies.get(nothing) // > undefined // 读取所有可见的cookie Cookies.get() // 删除某项co…...

【实战】SpringBoot自定义 starter及使用

文章目录 前言技术积累SpringBoot starter简介starter的开发步骤 实战演示自定义starter的使用写在最后 前言 各位大佬在使用springboot或者springcloud的时候都会根据需求引入各种starter&#xff0c;比如gateway、feign、web、test等等的插件。当然&#xff0c;在实际的业务…...

网络爬虫采集工具

在当今数字化的时代&#xff0c;获取海量数据对于企业、学术界和个人都至关重要。网络爬虫成为一种强大的工具&#xff0c;能够从互联网上抓取并提取所需的信息。本文将专心分享关于网络爬虫采集数据的全面指南&#xff0c;深入探讨其原理、应用场景以及使用过程中可能遇到的挑…...

【协议】XMLHttpRequest的梳理和总结

1. 前言 本篇梳理和总结一下XMLHttpRequest。 2. XMLHttpRequest原型对象的属性和方法 属性和方法说明示例new XMLHttpRequest() 功能&#xff1a;创建XHR对象 输入&#xff1a; 输出&#xff1a;XHR实例化对象 <略> XMLHttpRequest.prototype .open(method, url, asyn…...

AI教我学编程之C#类的基本概念(1)

前言 在AI教我学编程之C#类型 中&#xff0c;我们学习了C#类型的的基础知识&#xff0c;而类正是类型的一种. 目录 区分类和类型 什么是类&#xff1f; 对话AI 追问 实操 追踪属性的使用 AI登场 逐步推进 提出疑问 药不能停 终于实现 探索事件的使用 异步/交互操作 耗时操…...

前端js 数据结构:对象 object、数组Array 、Map 的创建、增删改 / 遍历数据

目录 前端js 数据结构&#xff1a;对象、数组、Map 的使用1 对象&#xff08;object&#xff09;1.1 创建对象1.1.1 对象字面量(最常用): {}1.1.2 使用 new 关键字和对象构造函数1.1.3 Object.create() 1.2 修改对象1.2.1 直接赋值&#xff1a;对象的属性名直接赋值1.2.2 点号/…...

ARM_Linux的NFS网络文件系统的搭建

介绍&#xff1a; NFS是network filesystem的简称&#xff0c;可以不同的主机通过网络访问远端的NFS服务器共享出来的文件&#xff0c;这样主机通过网络访问NFS服务器&#xff0c;我们就可以在开发板上通过网络访问主机的文件。 为什么要使用NFS网络文件呐&#xff1f; 1、传…...

vscode配置web开发环境(WampServer)

这里直接去下载了集成的服务器组件wampserver&#xff0c;集成了php&#xff0c;MySQL&#xff0c;Apache 可能会出现安装问题&#xff0c;这里说只有图上这些VC包都安装了才能继续安装&#xff0c;进入报错里提供的链接 在页面内搜索相关信息 github上不去可以去镜像站 下载…...

00-Rust前言

问&#xff1a;为什么要近期想学习Rust? 答&#xff1a; Rust出来也是有一段时间了&#xff0c;从Microsoft吵着要重构他们的C"祖传代码"开始&#xff0c;Rust就披着“高效&#xff0c;安全”的头衔。而自己决定要学习Rust&#xff0c;是因为近期发现&#xff1a;涉…...

3.conda的使用

anaconda安装 ubuntu 安装conda 系统架构 uname -m打开终端&#xff0c;不启动base conda config --set auto_activate_base falseconda命令使用 1.查看conda版本 conda --version2.查看conda配置环境 conda config --show3.设置镜像 #设置清华镜像 conda config --add…...

IPv6自动隧道---6to4中继

6to4中继 普通IPv6网络需要与6to4网络通过IPv4网络互通,这可以通过6to4中继路由器方式实现。所谓6to4中继,就是通过6to4隧道转发的IPv6报文的目的地址不是6to4地址,但转发的下一跳是6to4地址,该下一跳为路由器我们称之为6to4中继。隧道的IPv4目的地址依然从下一跳的6to4地…...

低代码开发:解锁数字化转型新维度

在信息化浪潮中&#xff0c;企业正面临着前所未有的挑战与机遇。一方面&#xff0c;市场环境瞬息万变&#xff0c;业务需求迭代频繁&#xff0c;对快速应用开发提出了更高要求&#xff1b;另一方面&#xff0c;传统软件开发模式受限于高成本、长周期等瓶颈&#xff0c;难以满足…...

Neeshck-Z-lmage_LYX_v2多场景落地:LoRA动态加载赋能数字人直播背景实时生成系统

Neeshck-Z-lmage_LYX_v2多场景落地&#xff1a;LoRA动态加载赋能数字人直播背景实时生成系统 1. 项目简介&#xff1a;一个专为本地绘画优化的轻量级工具 如果你对AI绘画感兴趣&#xff0c;特别是想体验国产的Z-Image文生图模型&#xff0c;但又被复杂的部署流程、繁琐的参数…...

AI读脸术多国面孔适配:跨种族识别优化部署实战

AI读脸术多国面孔适配&#xff1a;跨种族识别优化部署实战 1. 引言 你有没有遇到过这样的情况&#xff1a;一个在亚洲人脸识别上表现不错的AI模型&#xff0c;拿到一张欧洲人或非洲人的照片时&#xff0c;识别结果就开始"犯迷糊"了&#xff1f;性别判断出错&#x…...

57:L构建紫队协同:蓝队的协同防御

作者&#xff1a; HOS(安全风信子) 日期&#xff1a; 2026-03-07 主要来源平台&#xff1a; GitHub 摘要&#xff1a; 传统的红队和蓝队分离模式存在沟通障碍&#xff0c;导致防御效率低下。L构建了一套紫队协同系统&#xff0c;通过AI驱动的团队协作、知识共享和防御优化&…...

CHORD-X深度研究报告生成:集成MySQL进行数据存储与管理的配置指南

CHORD-X深度研究报告生成&#xff1a;集成MySQL进行数据存储与管理的配置指南 如果你正在使用CHORD-X这类强大的研究报告生成工具&#xff0c;可能会遇到一个甜蜜的烦恼&#xff1a;生成的内容越来越多&#xff0c;数据越来越杂&#xff0c;怎么才能把它们管得井井有条&#x…...

Z-Image-GGUF模型Java后端集成指南:SpringBoot微服务实战

Z-Image-GGUF模型Java后端集成指南&#xff1a;SpringBoot微服务实战 最近在做一个内容创作平台的后台重构&#xff0c;产品经理提了个需求&#xff0c;想给用户加个“AI一键生成文章配图”的功能。团队评估了几个方案&#xff0c;最终决定用Z-Image-GGUF这个模型&#xff0c;…...

RS232 vs RS485 vs TTL:如何为你的嵌入式项目选择正确的电平标准?

RS232 vs RS485 vs TTL&#xff1a;嵌入式工程师的电平标准选型指南 在嵌入式系统开发中&#xff0c;选择合适的电平标准往往决定了整个通信系统的可靠性和成本效益。就像建筑师需要根据不同的地质条件选择合适的地基方案一样&#xff0c;工程师也需要根据传输距离、环境干扰和…...

终极指南:如何使用RPGMakerDecrypter轻松解密游戏资源

终极指南&#xff1a;如何使用RPGMakerDecrypter轻松解密游戏资源 【免费下载链接】RPGMakerDecrypter Tool for extracting RPG Maker XP, VX and VX Ace encrypted archives. 项目地址: https://gitcode.com/gh_mirrors/rp/RPGMakerDecrypter RPGMakerDecrypter是一款…...

Leaf控制台终极指南:实时监控游戏服务器运行状态的完整教程

Leaf控制台终极指南&#xff1a;实时监控游戏服务器运行状态的完整教程 【免费下载链接】leaf A game server framework in Go (golang) 项目地址: https://gitcode.com/gh_mirrors/lea/leaf Leaf控制台是Go语言游戏服务器框架Leaf的强大实时监控工具&#xff0c;为游戏…...

【Java边缘运行时部署终极指南】:20年专家亲授5大避坑法则与3步极速上线实战

第一章&#xff1a;Java边缘运行时部署全景认知与演进脉络Java在边缘计算场景中的运行时部署正经历从传统云中心化架构向轻量、自治、低延迟方向的深刻演进。早期Java应用依赖完整JDK和重量级容器&#xff08;如Tomcat&#xff09;部署于虚拟机或Kubernetes集群&#xff0c;难以…...

事件驱动RTOS EventOS的创新设计与应用实践

1. 事件驱动型RTOS的创新设计 在嵌入式系统开发领域&#xff0c;实时操作系统(RTOS)一直是关键基础设施。传统RTOS如FreeRTOS、uC/OS等大多采用基于时间片轮转的任务调度机制&#xff0c;而EventOS则开创性地采用了事件驱动架构&#xff0c;这在资源受限的嵌入式环境中具有独特…...