当前位置: 首页 > 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;难以满足…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

python打卡day49

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...