库存超卖问题解决方式

news2024/10/2 6:26:49

文章目录

  • 超卖问题解决方式
    • 什么是库存超卖问题?
    • 乐观锁和悲观锁的定义
    • 超卖问题解决方式
      • 一、悲观锁
        • 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_stock
        set 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.coloradmin.cn/o/1979345.html

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

相关文章

Problems with OpenAI Authentification

题意&#xff1a;OpenAI 认证问题 问题背景&#xff1a; Ive got some problems with the Authentification to OpenAI in my python code. It seems like, OpenAI doesnt accept my key. I did a new on and tried it with other ones before. I always get the same issues.…

机械学习—零基础学习日志(高数21——泰勒展开)

零基础为了学人工智能&#xff0c;真的开始复习高数 求解自然对话e&#xff0c;证明e是如何从极限&#xff08;11/x&#xff09;x中得来的&#xff0c;需要了解泰勒展开式。现在终于学习到泰勒展开式。 泰勒公式定义 泰勒公式具体展开 泰勒公式考点常用 这里也可以等价到函数…

转录组差异分析方法整理(deseq2,edgeR,limma_voom)

三种最常用的差异分析方法(deseq2&#xff0c;edgeR&#xff0c;limma_voom)的整理。 目前在实际应用的过程中一般选择其中一种结果即可&#xff0c;或三种方法分析后结果取交集。 本次演示选择了GSE213615数据集&#xff0c;该数据集采用了两种肝癌细胞系&#xff0c;并使用索…

鸿蒙媒体开发【相机数据采集保存】音频和视频

相机数据采集保存 介绍 本示例主要展示了相机的相关功能&#xff0c;使用libohcamera.so 接口实现相机的预览、拍照、录像、前后置摄像头切换进行拍照、录像&#xff0c;以及对焦、曝光等控制类功能。 效果预览 使用说明 弹出是否允许“CameraSample”使用相机&#xff1f;…

CentOS上面的MySQL安装~~~保姆级教程

目录 0.声明 1.下载官网包包 2.新建文件夹&#xff0c;把rpm拖拽进来 3.安装yum源&#xff0c;查看前后变化 4.安装mysql服务 5.查看是否安装成功 6.出现报错的解决方案 7.启动MySQL 8.查看启动服务 9.配置文件 10.重新运行 11.免密码登录 12.再谈配置文件 0.声明…

【Unity】3D功能开发入门系列(四)

Unity3D功能开发入门系列&#xff08;四&#xff09; 一、组件的访问&#xff08;一&#xff09;组件的调用&#xff08;二&#xff09;组件的参数&#xff08;三&#xff09;引用别的组件&#xff08;四&#xff09;引用脚本组件&#xff08;五&#xff09;消息调用 二、物体的…

A*搜索算法 双向A*搜索算法 Julia实现

算法概述 抽象的在非负有向边权图 G ( V , E , W ) , W : E → R G (V, E, W), W: E \to \mathbb{R} G(V,E,W),W:E→R 上的 BFS 过程可以看作这样&#xff1a; (1) 设 C C C 点集表示已遍历的点&#xff0c; ∀ n ∈ C , d ( n ) \forall n \in C, d(n) ∀n∈C,d(n) 表示…

Leetcode75- 种花问题

间隔种花 也就是 0 0 0 或者开头 0 0 结尾 0 0 也就是这三个地方可以种花 然后分别判断 最后根据提交结果分析漏掉的情况 比如 n为0 和 数组长度只有一个的情况 使用枚举可以很快解题

技术男的审美反击:UI配置化新纪元

之前常常被甲方的领导说&#xff0c;我们全是一群钢铁直男&#xff0c;一点不懂审美&#xff0c;其实我们心里边想的 “您说得对啊&#xff01;&#xff01;&#xff01;&#xff01;” 这个可能和理工科有关系吧&#xff0c;理工男好像都差不多&#xff0c;所以这次我们就把很…

Vue的学习(二)

目录 一、class及style的绑定 1.v-bind:class绑定类名 绑定class为对象 ​编辑2. v-bind:class绑定类名 绑定class为对象 3.v-bind:class绑定类名 绑定class为数组 1) v-bind:class绑定类名 绑定class为数组 方法一&#xff1a; 2) v-bind:class绑定类名 绑定class为数组…

实验4-2-1 求e的近似值

//实验4-2-1 求e的近似值 /* 自然常数 e 可以用级数 11/1!1/2!⋯1/n!⋯ 来近似计算。 本题要求对给定的非负整数 n&#xff0c;求该级数的前 n1 项和。 输入格式:输入第一行中给出非负整数 n&#xff08;≤1000&#xff09;。 输出格式:在一行中输出部分和的值&#xff0c;保留…

nginx: [error] open() “/run/nginx.pid“ failed (2: No such file or directory)

今天 准备访问下Nginx服务&#xff0c;但是 启动时出现如下报错&#xff1a;&#xff08;80端口被占用&#xff0c;没有找到nginx.pid文件&#xff09; 解决思路&#xff1a; 1、 查看下排查下nginx服务 #确认下nginx状态 ps -ef|grep nginx systemctl status nginx#查看端口…

数据结构——时间和空间复杂度

目录 一、时间复杂度和空间复杂度是什么 二、为什么要有时间复杂度和空间复杂度 三、时间复杂度 四、空间复杂度 一、时间复杂度和空间复杂度是什么 在生活中&#xff0c;我们做一件事情需要花费一定的时间和一定的空间&#xff0c;举一个例子&#xff1a; 一个工厂需要制…

从根儿上学习spring 十一 之run方法启动第四段(5)

图15-AbstractAutowireCapableBeanFactory#doCreateBean方法 我们接着讲doCreateBean方法&#xff0c;之前对循环依赖做了些解释&#xff0c;我们接着往下看populateBean(beanName, mbd, instanceWrapper)方法 图15-572行 这行就是调用populateBean(beanName, mbd, instanceW…

目标检测——YOLOv10: Real-Time End-to-End Object Detection

YOLOv10是在YOLOv8的基础上&#xff0c;借鉴了RT-DETR的一些创新点改进出来的 标题&#xff1a;YOLOv10: Real-Time End-to-End Object Detection论文&#xff1a;https://arxiv.org/pdf/2405.14458源码&#xff1a;https://github.com/THU-MIG/yolov10 1. 论文介绍 在过去的几…

【C语言】详解feof函数和ferror函数

文章目录 前言1. feof1.1 feof函数原型1.2 正确利用函数特性读写文件1.2.1 针对文本文件1.2.2 针对二进制文件 1.3 feof函数的原理1.4 feof函数实例演示 2. ferror2.1 ferror函数原型 前言 或许我们曾在网络上看过有关于feof函数&#xff0c;都说这个函数是检查文件是否已经读…

Windows系统使用内网穿透配置Mysql公网地址实现IDEA远程连接

文章目录 前言1. 本地连接测试2. Windows安装Cpolar3. 配置Mysql公网地址4. IDEA远程连接Mysql5. 固定连接公网地址6. 固定地址连接测试 前言 IDEA作为Java开发最主力的工具&#xff0c;在开发过程中需要经常用到数据库&#xff0c;如Mysql数据库&#xff0c;但是在IDEA中只能…

【Python学习手册(第四版)】学习笔记15-文档(注释、PyDoc等)

个人总结难免疏漏&#xff0c;请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。 本文主要介绍程序的文档概念。包括为程序编写的注释&#xff0c;以及内置工具的文档。讲解文档字符串、Python的在线手册等资源、以及PyDoc的help函数和网页接口。…

蒙电通无人机航线规划系统 雷达点云电力应用软件

蒙电通无人机航线规划系统&#xff0c;它可进行标记杆塔、切档、自动对点云数据分类和点云抽稀等处理&#xff0c;按3张或6张照片自动生成航线&#xff0c;或按自定义航线模型生成航线&#xff0c;支持安全性检测。在满足当地巡检标准的前提下&#xff0c;系统操作非常简便。 …

llama神经网络的结构,llama-3-8b.layers=32 llama-3-70b.layers=80; 2000汉字举例说明

目录 llama-3-8b.layers=32 llama-3-70b.layers=80 llama神经网络的结构 Llama神经网络结构示例 示例中的输入输出大小 实际举例说明2000个汉字文本数据集 初始化词嵌入矩阵 1. 输入层 2. 嵌入层 3. 卷积层 4. 全连接层 llama-3-8b.layers=32 llama-3-70b.laye…