Redis高并发分布式锁

news/2024/5/9 16:14:15/文章来源:https://blog.csdn.net/weixin_44906791/article/details/131449957

文章目录

  • 高并发场景秒杀抢购超卖Bug
    • 高并发场景秒杀抢购Demo
    • 测试结果
  • JVM级别锁
    • 使用nginx对本地服务进行负载均衡
  • Redis实现分布式锁
    • Redis分布式锁实现Demo
    • Redis分布式锁有关问题
  • 分布式锁性能的提升
    • 减少锁的粒度
    • 使用异步处理


高并发场景秒杀抢购超卖Bug

  在今天的数字化世界中,高并发已经成为了许多在线应用的常态,尤其是在电商平台的秒杀活动、票务系统的抢票环节,或者任何需要处理大量用户请求的场景中。然而,高并发也带来了一系列的挑战,其中最常见的就是超卖问题。

  想象一下,你正在举办一个大型的秒杀活动,数万名用户在同一时间抢购同一款限量的商品。在理想的情况下,系统应该能够正确地处理所有的请求,确保商品的库存数量不会被超额扣减。然而,现实情况往往并非如此。如果没有有效的并发控制机制,你的系统可能会在短时间内接收到大量的购买请求,导致商品的库存数量被超额扣减,即出现超卖现象。这不仅会影响到你的业务运营,也会对用户体验产生负面影响。

高并发场景秒杀抢购Demo

以下是一个高并发场景场景秒杀抢购的一个Demo:

这里我用了一个子项目继承了父项目

父pom.xml

    <dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.1</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

子pom.xml

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency></dependencies>

下面就是代码了:

StockController.class

@RestController
@RequestMapping("/stock")
public class StockController {@AutowiredStockService stockService;@RequestMapping("/deduct")public String deductStock(){return stockService.deductStock();}
}

StockService.class

@Service
public class StockService {@AutowiredStringRedisTemplate stringRedisTemplate;public String deductStock(){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if(stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("stock",realStock+"");System.out.println("扣减成功,剩余库存:"+realStock);}else {System.out.println("扣减失败,库存不足");}return "end";}
}

这里使用了redis,给redis中stock的值是200
在这里插入图片描述
然后用Apache JMeter压测工具进行压测,我这里是让500个线程在1s之内启动然后去争抢200个库存

在这里插入图片描述

为了更直观的看出到底售卖出了多少商品,我在redis中存放了一个 keyokvalue0 的值,然后修改StockService,每次售卖出一件商品的时候给redis中的ok +1。
在这里插入图片描述

在这里插入图片描述

测试结果

从控制台的输出打印可以看出出现了大量的超卖问题,同一个商品被卖出了多次,200个商品卖完,竟然卖出了313件。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

那么,如何解决这个问题呢?

JVM级别锁

学过并发,会想到使用synchronized或者ReentrantLock,这样的JVM级别的锁来解决这个问题。
在这里插入图片描述
进行压测会发现问题解决了,200件商品的确是卖出去了200次,并没有出现超卖问题。但是如果在高并发场景下,使用了分布式架构。有多个tomcat,JVM级别的锁还有用吗。

使用nginx对本地服务进行负载均衡

这里我启动了两个服务器,使用了不同端口,一个8080,一个8090
在这里插入图片描述
然后本地启动了一台虚拟机,这台虚拟机用来做本地的负载均衡使用
在这里插入图片描述
虚拟机已经安装好了nginx,首先ipconfig找到本机的局域网ip地址,在nginx.conf 配置上对该ip地址的8080端口和8090端口进行负载均衡。
在这里插入图片描述

在这里插入图片描述
最后访问虚拟机的局域网地址和nginx.conf 配置的端口号即可。虚拟机的局域网地址可以通过ifconfig命令查询
在这里插入图片描述
在这里插入图片描述
不停的访问该地址可以看到两个服务器上都有打印输出,现在对该接口进行压测发现200个商品被卖出去了245次。
在这里插入图片描述
JVM级别的锁只能管理本tomcat的线程,其他服务器的线程是没有办法管理的。那应该使用什么呢?

Redis实现分布式锁

Redis提供了一种名为SETNX的命令,可以用来实现分布式锁。
在这里插入图片描述

Redis分布式锁实现Demo

@Service
public class StockService {@AutowiredStringRedisTemplate stringRedisTemplate;public String deductStock() {String lockKey = "lock:product_1";Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "root");if (!result) {return "error_code";}int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock > 0) {int realStock = stock - 1;stringRedisTemplate.opsForValue().set("stock", realStock + "");stringRedisTemplate.opsForValue().increment("ok");System.out.println("扣减成功,剩余库存:" + realStock);} else {System.out.println("扣减失败,库存不足");}stringRedisTemplate.delete(lockKey);return "end";}
}

Redis分布式锁有关问题

当然这里还有很多问题
问题1:通过SETNX上锁以后,中间代码如果出现异常,导致后面的删除锁代码无法执行,导致死锁
解决方法:添加try-catch,并将删除锁代码放在finally中,这样无论有没有抛异常都会释放锁。
在这里插入图片描述
问题2:通过SETNX上锁以后,运行过程出现 宕机 ,系统被 重启 ,同样将导致 死锁
解决方法:给锁 设置超时时间,过了一段时间以后,锁将自动释放。
在这里插入图片描述
问题3:通过SETNX上锁以后 出现异常 ,导致无法去expire设置锁的超时时间,也没有办法去手动释放锁,导致 死锁
解决方法:保证上锁和设置超时时间原子性,使用Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit);方法。

在这里插入图片描述
问题4:线程A中 上锁设置过期时间 完成以后,系统出现阻塞,导致锁已经到了过期时间并自动删除了,这时候还没执行释放锁的操作,这时候线程B上锁成功,并执行任务,结果线程A反应过来了,继续执行释放锁的操作,把线程B上的锁给释放了,后面的线程上的锁都被前面的线程释放。
解决方法:这个问题的根本点在于,线程可以释放其他线程所加的锁,可以给锁添加uuid让线程只能释放自己加的锁
在这里插入图片描述
问题5:线程A 上锁设置过期时间并执行任务后,希望判断比较锁的id之后去释放锁,判断通过以后系统出现阻塞,阻塞到 锁已经过期了,但是此时并未执行释放锁的操作,此时线程B上锁成功,并去执行任务。线程A反应了过来,然后将线程B上的锁给释放了,这时候又将出现上面的问题。
解决方法:这个问题的根本原因在于 最后判断锁id的时候和释放锁的操作没有保证 原子性 。使用redis执行LUA脚本,保证 能同时执行判断锁和释放锁。这个redis并没有提供方法。
问题6锁续命,线程A任务还没执行完,锁已经过期了,此时其他线程也会执行任务,就会出现并发安全问题。
解决方法:可以使用WatchDog,也就是给任务执行线程添加守护线程,守护线程负责对锁的expire时间进行监控,每当到过期前一秒就对过期进行判断,如果任务还在进行且锁马上过期,就对过期时间进行设置。
上面的问题5问题6都可以通过redis组件,redisson解决
在这里插入图片描述

在这里插入图片描述

分布式锁性能的提升

减少锁的粒度

上述场景可以使用分段锁。基于加锁的分段机制,可以给分布式锁的性能提升几十倍。将一个商品分成好几个key。比如一个商品1000个库存,拆成10个key,每个key放100个库存。
实际实现还是有许多细节,比如对key的轮询选择,如果一个key库存为0了,就不应该再选择这个库存了。如果每个key库存都只有1,要减少5个库存的解决。实现可以参考ConcurrentHashMap1.7的源码。

使用异步处理

你可以使用消息队列等技术,将请求的处理过程异步化。当一个请求到达时,你只需要将它放入消息队列,然后立即返回。这样,你的系统就可以快速地处理大量的并发请求。然后,你可以在后台有一个或多个工作线程,从消息队列中取出请求并处理。


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

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

相关文章

Python操作SQLite数据库

文章目录 这篇博客很简单&#xff0c;简单记录下SQLite基础使用。有些数据文件是.db为扩展名的&#xff0c;要用到SQLite进行读写和增删改查操作。SQLite数据库是一种轻量级的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;以单个文件的形式存储&#xff0c;整…

【数据挖掘】时间序列教程【二】

2.4 示例&#xff1a;颗粒物浓度 在本章中&#xff0c;我们将使用美国环境保护署的一些空气污染数据作为运行样本。该数据集由 2 年和 5 年空气动力学直径小于或等于 3.2017 \&#xff08;mu\&#xff09;g/m\&#xff08;^2018\&#xff09; 的颗粒物组成。 我们将特别关注来自…

优盘数据恢复怎么做?3个方法分享!

我的优盘里保存了很多有纪念意义的照片&#xff0c;但是刚刚将u盘插入电脑后&#xff0c;发现有些照片已经损坏了。我想将优盘里的数据恢复&#xff0c;有什么靠谱的方法吗&#xff1f;给我推荐一下吧&#xff01; 优盘是一种便携式存储设备&#xff0c;常用于存储和传输数据。…

Linux v4l2框架分析

1. 概述 V4L2(Video for Linux 2)&#xff1a;Linux内核中关于视频设备驱动的框架&#xff0c;对上向应用层提供统一的接口&#xff0c;对下支持各类复杂硬件的灵活扩展&#xff1b; V4L2框架&#xff0c;主要包括v4l2-core、meida framework、videobuf2等模块&#xff0c;这也…

攻防世界-Crypto-Normal_RSA

题目描述&#xff1a;下载附件后&#xff0c;附件中只有两个文件&#xff0c;一个是加密后的密钥&#xff0c;一个是公钥文件 背景知识&#xff1a;RSA加密算法 现在相当于给出了密文和公钥&#xff0c;需要我们去求解明文 1. 思路分析 既然要解密&#xff0c;那么必须要获取…

Facebook Insights分析工具解读,掌握关键数据指标

什么是Facebook Insights&#xff1f; Facebook Insights是Facebook平台上的一项内置分析工具&#xff0c;旨在帮助企业和品牌了解其在Facebook上的表现和受众互动情况。该工具提供了丰富的数据和指标&#xff0c;可以帮助用户洞察粉丝群体、了解发布内容的表现&#xff0c;并…

Hutool 30

Hutool是一个Java工具包&#xff0c;提供了丰富的工具类库和简化开发的工具方法。它的目标是提供一套丰富、实用、高效和易用的Java工具类&#xff0c;以提升开发者的开发效率和开发质量。以下是Hutool的一些主要特点和功能模块&#xff1a; 常用工具类&#xff1a;Hutool提供了…

百度智能车竞赛丝绸之路2——手柄控制

百度智能车竞赛丝绸之路1——智能车设计与编程实现控制 百度智能车竞赛丝绸之路2——手柄控制 一、机器人设计 二、实现原理 本教程使用Python的Serial库和Struct二进制数据解析库去实现Xbox手柄百度大脑学习开发板&#xff08;上位机&#xff09;和机器人控制器&#xff08;…

你的服务器还安全吗?用户数据是否面临泄露风险?

一系列严重的网络安全事件引起了广泛关注&#xff0c;多家知名公司的服务器遭到黑客挟持&#xff0c;用户的个人数据和敏感信息面临泄露的风险。这些事件揭示了网络安全的脆弱性和黑客攻击的威胁性&#xff0c;提醒着企业和个人加强对网络安全的重视。 一、入侵案例1.1 蔚来数据…

Pycharm中成功配置PyQt5(External Tools),设计好界面直接生成python代码

1、安装PyQt5和PyQt5-tools 在Pycharm中设置好Python环境&#xff0c;点击File-Settings-Project-Python Interpreter 设置好后退出&#xff0c;点击窗口下的Terminal&#xff0c;输入 # 直接安装输入pip install pyqt5&#xff0c;如果太慢可以用国内镜像源&#xff0c;若出…

PS扣签名

打开Photoshop CS6&#xff0c;依次点击“文件”-“打开”&#xff0c;把签名照导入进来。 在“选择”菜单下点击“色彩范围”。 此时鼠标形状变成了一支笔&#xff0c;点击签名上黑色的地方&#xff0c;适当调整颜色容差&#xff0c;点击“确定”完成选择。 按住CtrlJ组…

Postman设置断言

目录 前言&#xff1a; 一、断言的定义 二、Postman断言的语法 三、Postman中chai.js断言常用语法 前言&#xff1a; 在进行API测试时&#xff0c;断言是一项重要的功能。它能帮助我们验证接口的响应是否符合预期结果&#xff0c;从而确保API的正确性和可靠性。在Postman中…

【微服务】什么是微服务?-- 全面了解微服务架构

What is Microservices — Edureka 您有没有想过&#xff0c;什么是微服务以及扩展行业如何与它们集成&#xff0c;同时构建应用程序以满足客户的期望&#xff1f; 要了解什么是微服务&#xff0c;您必须了解如何将单体应用程序分解为独立打包和部署的小型微型应用程序。本文将…

Electron + ts + vue3 + vite 项目搭建

Electron 是一个基于 Chromium 和 Node.js 的桌面应用程序开发框架&#xff0c;而 Vue3 则是一种流行的前端框架。将两者结合使用可以快速地打造出跨平台的桌面应用程序。在这种组合中&#xff0c;Electron 提供了强大的桌面应用开发能力&#xff0c;而 Vue3 则提供了易用的 UI…

【2023年江西省研究生数学建模竞赛】题目一 蒸汽发生器倒U型管内液体流动 建模方案及参考文献

代码与结果如下&#xff1a;完整文档见文末 完整思路”请点击这里“到原文章获取 题目&#xff1a; PACTEL压水堆整体测试设备在2009年建造&#xff0c;用于带有垂直倒U型管蒸汽发生器的压水堆热液压相关的安全性研究,参见图1。 PACTEL压水堆设施包括一个反应堆压力容器模型…

机器学习技术(一)——python基础超详解

机器学习技术&#xff08;一&#xff09;——python基础超详解 文章目录 机器学习技术&#xff08;一&#xff09;——python基础超详解0、引言1、基础概念**:snake:变量****:snake:注释****:snake:输入 输出** 2、数据类型**:snake:数值类型****:snake:运算符****:snake:字符串…

Matlab论文插图绘制模板第106期—带误差棒的堆叠柱状图

在之前的文章中&#xff0c;分享了Matlab带误差棒的折线图绘制模板&#xff1a; 带误差棒的柱状图绘制模板&#xff1a; 进一步&#xff0c;再来分享一下带误差棒的堆叠柱状图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源…

《移动互联网技术》第一章 概述: 掌握移动互联网的基本概念和组成

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

基于Java+Swing+Mysql商品信息管理系统

基于JavaSwingMysql商品信息管理系统 一、系统介绍二、功能展示1.主页2.新增商品信息3.查询商品信息 三、数据库四、其他系统实现五、获取源码 一、系统介绍 该系统实现了查看商品列表、新增商品信息、查询商品信息 运行环境&#xff1a;eclipse、idea、jdk1.8 二、功能展示…