库存超卖问题解决方式

文章目录

  • 超卖问题解决方式
    • 什么是库存超卖问题?
    • 乐观锁和悲观锁的定义
    • 超卖问题解决方式
      • 一、悲观锁
        • 1.jvm单机锁
        • 2.通过使用mysql的行锁,使用一个sql解决并发访问问题
        • 3.使用mysql的悲观锁解决
        • 4. 使用redis分布式锁来解决
      • 二、乐观锁解决
        • 1.版本号
        • 2. CAS法(Compare and Switch or Set)。

超卖问题解决方式

什么是库存超卖问题?

库存超卖是指多个请求同时减少库存时,库存数量变为负数的情况。例如,某商品库存数量为10,同时有两个请求减少库存数量,假设两个请求同时查询库存数量为1,然后各自减少库存数量,最后库存数量变为-1,这就是超卖问题。

乐观锁和悲观锁的定义

  • 悲观锁:
    认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。例如Synchronized、Lock都属于悲观锁。

    优点:简单粗暴
    缺点:性能一般

  • 乐观锁:
    认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改。如果没有修改则认为是安全的,自己才更新数据。如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。

    优点:性能好
    缺点:存在成功率低的问题

超卖问题解决方式

一、悲观锁

1.jvm单机锁

认为线程问题一定会发生,因此在操作数据库之前先获取锁,确保线程串行执行,例如synchronized关键字和ReentrantLock可重入锁。

2.通过使用mysql的行锁,使用一个sql解决并发访问问题
  • 通过sql扣减库存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ht.atp.plat.mapper.WmsStockMapper"><update id="checkAndReduceStock">update wms_stockset stock_quantity = (stock_quantity - #{reduceStock})where id = 1 and (stock_quantity - #{reduceStock}) >= 0;</update>
</mapper>
3.使用mysql的悲观锁解决
  • 使用for update加锁查询库存
    在这里插入图片描述

注意:

需要注意的是,该查询库存和更新库存的操作必须放在同一个本地事务中,否则悲观锁将失效。悲观锁只有在本次操作全部完成事务提交之后才会释放锁。如果不在同一个事务中,锁提前释放去更新库存还是会存在并发的问题。

4. 使用redis分布式锁来解决
  • 基于redis实现分布式锁

重要指令:set lock_name unique_value NX PX
解释:
unique_value:表示客户端编码,一定要具有唯一性,在解锁的时候,需要验证value和加锁的一致才允许删除key
解决超时问题:第一个进程加锁释放了,但还未执行业务逻辑,第二个进程就获取了锁
NX:若key不存在就设置成功,若存在就返回true
PX:指定过期时间

  • 基于redisson开源框架实现
RLock lock =redisson.getLock("huaweiLock");
//只有一个客户端可以成功加锁
lock.lock(); //执行业务逻辑
检查库存
创建订单
扣减库存
更新redis//释放锁,其他客户端可以尝试加锁
lock.unlock();

redis分布式锁的缺点:

redis分布式锁 锁住的资源是,同一个商品的库存多用户同时下单的时候,会基于分布式锁串行化处理,导致没法同时处理同一个商品的大量下单的请求。

redis分布式锁优化:

  • 其实说出来也很简单,相信很多人看过java里的ConcurrentHashMap的源码和底层原理,应该知道里面的核心思路,就是分段加锁!

  • Java 8中新增了一个LongAdder类,也是针对Java7以前的AtomicLong进行的优化,解决的是CAS类操作在高并发场景下,使用乐观锁思路,会导致大量线程长时间重复循环。

  • LongAdder中也是采用了类似的分段CAS操作,失败则自动迁移到下一个分段进行CAS的思路

  • 其实这就是分段加锁。你想,假如你现在iphone有1000个库存,那么你完全可以给拆成20个库存段,要是你愿意,可以在数据库的表里建20个库存字段,比如stock_01,stock_02,类似这样的,也可以在redis之类的地方放20个库存key。

  • 总之,就是把你的1000件库存给他拆开,每个库存段是50件库存,比如stock_01对应50件库存,stock_02对应50件库存。
    接着,每秒1000个请求过来了,好!此时其实可以是自己写一个简单的随机算法,每个请求都是随机在20个分段库存里,选择一个进行加锁。

  • bingo!这样就好了,同时可以有最多20个下单请求一起执行,每个下单请求锁了一个库存分段,然后在业务逻辑里面,就对数据库或者是Redis中的那个分段库存进行操作即可,包括查库存

  • 一旦对某个数据做了分段处理之后,有一个坑大家一定要注意:就是如果某个下单请求,咔嚓加锁,然后发现这个分段库存里的库存不足了,此时咋办?
    这时你得自动释放锁,然后立马换下一个分段库存,再次尝试加锁后尝试处理。这个过程一定要实现。

redis分布式锁优化缺点:
首先,你得对一个数据分段存储,一个库存字段本来好好的,现在要分为20个分段库存字段;
其次,你在每次处理库存的时候,还得自己写随机算法,随机挑选一个分段来处理;
最后,如果某个分段中的数据不足了,你还得自动切换到下一个分段数据去处理

二、乐观锁解决

1.版本号

给商品加上版本号字段,如果查询到就让其version=1,在修改执行的时候,先判断版本号是不是正确的,如果是让其版本号发生变化,并执行扣减,如果不是就说明当前商品已经卖出。

在这里插入图片描述

2. CAS法(Compare and Switch or Set)。

CAS流程如下:

  • 获取目标内存位置的当前值。
  • 检查当前值是否与预期值相等。
  • 如果相等,则将新值写入目标内存位置;否则,放弃写入操作,可能是重新读取当前值并重试整个CAS操作。

比如当前的订单系统中,就可以使用查询到的库存作为预期值,修改的时候进行判定,如果是库存和第一次查询到的一样就执行,不一样就取消执行,这样就能够保证原子性

在这里插入图片描述

  • 代码1:
  boolean success = seckillVoucherService.update().setSql("stock= stock -1") //set stick = stock - 1.eq("voucher_id", voucherId).eq("stock", voucher.getStock()) // where id = ? and stock = ?.update();

这种方法结果测试发现出错率较高。比如同时刻有100个请求,第一个请求成功修改库存,但剩余99个请求在乐观锁判断时都发现数据库的库存数据和原先获取的不一致,导致无法通过,事实上我们在修改数据时只需要判断库存是否大于零即可。

  • 代码2:
  boolean success = seckillVoucherService.update().setSql("stock= stock -1") //set stick = stock - 1.eq("voucher_id", voucherId).gt("stock",0).update();

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

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

相关文章

数据结构第1天作业 7月31日

2.3按位置操作 1&#xff09;按照位置插入数据 void Insert_seqlist_single(Seqlist* sq,int arr_sub,int num){if(sq->posN ){ //判断顺序列表是否为满printf("error");return;}else if(arr_sub<0||arr_sub>sq->pos){printf("error…

React组件生命周期

一张图解释 React 类组件生命周期方法 React 类组件的生命周期可以分为三个主要阶段&#xff1a; 挂载&#xff08;Mounting&#xff09; 更新&#xff08;Updating&#xff09; 卸载&#xff08;Unmounting&#xff09; 挂载阶段 在组件实例被创建并插入到 DOM 中时调用…

SpringSecurity登录认证流程及源码分析

目录 一 作用 二 流程及源码分析 一 作用 spring security作为spring家族中的一员&#xff0c;它的主要作用有两个&#xff0c;分别是认证和授权。 我们以前在实现登录功能的时候&#xff0c;前端会传来用户名和密码&#xff0c;然后我们根据前端传来的数据从用户表中的数据进…

Java高级Day18-集合

62.集合 之前保存多个数据元素使用数组&#xff0c;但数组有以下缺点&#xff1a; 长度开始必须指定&#xff0c;指定后不可修改 保存的必须为同一类型的元素 使用数组进行增加/删除元素的代码比较麻烦 集合 可以动态的保存任意多个对象 提供了一系列方便操作对象的方法 …

河南萌新联赛2024第(三)场:河南大学

传送门&#xff1a;河南萌新联赛2024第&#xff08;三&#xff09;场&#xff1a;河南大学_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ B 正则表达式 思路&#xff1a;模拟 代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long lo…

vue3+fetch请求+接收到流式的markdown数据+一边gpt打字机式输出内容,一边解析markdown语法+highlight.js实现代码高亮

这个问题终于解决了&#xff01;好开心。 先看最终效果&#xff1a; video_20240724_141543_edit 项目背景&#xff1a;vue3 场景&#xff1a;像gpt一样可以对话&#xff0c;当用户发送问题之后&#xff0c;ai回复&#xff0c;ai是一部分一部分回复&#xff0c;像打印机式输出…

Moving Object Segmentation: All You Need Is SAM(and Flow) 论文详解

系列文章目录 文章目录 系列文章目录前言摘要1 引言2 相关工作3 SAM Preliminaries4 帧级分割Ⅰ&#xff1a;以流作为输入5 帧级分割Ⅱ&#xff1a;以流为提示6 序列级掩膜关联7 实验7.1 数据集7.2 评价指标7 .3 实施细节7.4 消融实验7.5 定量结果7 .定性可视化 8 结论致谢附录…

Ollama0.3.0 + llama3.1本地部署

Ollama0.3.0 llama3.1本地部署 安装Ollama 官网下载安装包 https://ollama.com/download ​​ 根据系统选择对应版本 安装完成后&#xff0c;电脑右下角会出现ollama程序图标&#xff0c;表示ollama正在运行。 ​​ 打开cmd命令 下载Llama3.1 输入ollama&#xff0c…

从“线缆迷宫”到“数字通途”:一机一网助力成天泰园区网络升级

(文 林海宾/深圳速锦网络科技有限公司) 林海宾,现任深圳速锦网络科技有限公司(以下简称速锦网络)的项目总监,一个入行十年、经验老道的数字化升级”操盘手“。他曾经主导过中国农业银行深圳分行130多个网点以及美的珠海工厂等数字化建设升级项目。在2024年的五一,他帮助深圳市…

【MATLAB源码】机器视觉与图像识别技术(7)续---BP神经网络

系列文章目录在最后面&#xff0c;各位同仁感兴趣可以看看&#xff01; BP神经网络 第一节、BP网络定义第二节、BP网络结构及其特点第三节、信息传播方式 信息的正向传播&#xff1a;实质是计算网络的输出误差的反向传播&#xff1a;实质是学习过程第四节、 BP网络的算法流程…

视频怎么在尽量不损害画质的前提下压缩?试试这4款视频压缩神器

4个视频压缩神器&#xff0c;帮你在不损画质的前提下满足压缩需求&#xff1a; 1、嗨格式压缩大师 关键词&#xff1a;高效、批量 直达链接>>yasuo.hgs.cn 嗨格式压缩大师是一款免费的文件压缩工具&#xff0c;支持视频、图片、PDF、PPT等文件快速、批量压缩&#xff…

代码随想录 day 28 贪心

第八章 贪心算法 part02 贪心 局部最优解推出全局最优 &#xff0c;而且想不到反例&#xff0c;那么就试一试贪心 将问题分解为若干个子问题 找出适合的贪心策略 求解每一个子问题的最优解 将局部最优解堆叠成全局最优解 只要想清楚 局部最优 是什么&#xff0c;如果推导出全局…

XR-Frame 计算相机与场景物体的距离

如下哦 const cameraTransform this.scene.getElementById(camera).getComponent(transform)const modelTransform this.scene.getElementById(yourNodeId).getComponent("transform");if (cameraTransform.worldPosition.distanceTo(modelTransform.worldPosition…

pip install albumentations安装下载遇19kB/s超级慢细水管解决办法

albumentations 是一个用于图像增强的 Python 库&#xff0c;它提供了丰富的图像变换功能&#xff0c;可以用于数据增强&#xff0c;从而提高深度学习模型的泛化能力。 直接安装命令&#xff1a; pip install albumentations但是如果半夜遇到这种19kB/s的下载速度 为头发着想&…

【C++】C++11新增语法(右值引用、完美转法)

文章目录 1.C11新增常用语法1.1 统一的列表初始化1.2 initializer_list初始化1.3 声明相关1.4 继承与多态相关 2. 右值引用与移动语义2.1 左值引用与右值引用2.2 右值引用与移动语义的使用场景2.3 右值引用引用左值(move) 3. 完美转发4. 新的类功能4.1 新增两个默认成员函数4.2…

记录两道关于编码解码的问题

环境&#xff1a;php环境即可&#xff0c;也可使用phpstudy。 参考文章: 深入理解浏览器解析机制和XSS向量编码-CSDN博客(很重要) HTML 字符编码&#xff08;自我复习&#xff09;-CSDN博客 例题1&#xff1a; <?php header("X-XSS-Protection: 0"); $xss …

Jangow-1.0.1靶机漏洞复现(未完成)

首先&#xff0c;这个靶机只能使用VirtualBox打开&#xff0c;靶机下载地址为 https://download.vulnhub.com/jangow/jangow-01-1.0.1.ova 虚拟机软件下载地址为 Download_Old_Builds – Oracle VM VirtualBox 开启靶机后访问ip进入如下页面&#xff0c;点击site进入到一个…

昇思25天学习打卡营第16天|Diffusion扩散模型,DCGAN生成漫画头像

Diffusion扩散模型 关于扩散模型&#xff08;Diffusion Models&#xff09;有很多种理解&#xff0c;本文的介绍是基于denoising diffusion probabilistic model &#xff08;DDPM&#xff09;&#xff0c;DDPM已经在&#xff08;无&#xff09;条件图像/音频/视频生成领域取得…

MySQL:GROUP BY 分组查询

分组查询是SQL中一个非常强大的功能&#xff0c;它允许我们将数据按照一个或多个字段进行分组&#xff0c;并对每个分组进行聚合计算&#xff08;如求和、平均值、最大值、最小值等&#xff09;。在MySQL中&#xff0c;我们使用 GROUP BY 关键字来实现分组查询。 核心语法 SE…

Vue3自研开源Tree组件:人性化的拖拽API设计

针对Element Plus Tree组件拖拽功能API用的麻烦&#xff0c;小卷开发了一个API使用简单的JuanTree组件。拖拽功能用起来非常简单&#xff01; 文章目录 使用示例allowDragallowDrop支持节点勾选支持dirty检测后台API交互 源码实现 使用示例 组件的使用很简单&#xff1a; 通过…