【Redis】电商项目秒杀问题之超卖问题与一人一单问题

news2024/11/15 8:24:51

目录

一、超卖问题

1、背景

2、产生原因以及线程安全问题

3、解决

1.悲观锁

2.乐观锁

4、新的问题

5、解决

二、一人一单

1、背景

2、产生原因以及线程安全问题

3、解决

4、新的问题----集群下的并发安全问题

5、解决

三、集群下的并发问题

1、说明

2、解决


一、超卖问题

1、背景

在如双11等购物需求剧增的背景下,一个物品库存里有100件但是由于并发等问题可能会导致该物品被卖出超过100件。这就是超卖问题,他是由于库存量被高并发请求而产生的线程安全问题。

2、产生原因以及线程安全问题

当库存仅为1的时候,此时由于并发量高多个线程进行库存查询操作,此时这些线程都查询到库存为1,都各自去进行后续下单扣减库存的操作,此时库存被扣减多次成为负数,产生超卖问题,也就是线程安全问题 

3、解决

对于上述超卖的线程安全问题我们可以采用两种方式来解决

1.悲观锁

首先悲观锁的概念是他认为一定会产生线程安全问题所以采用直接加锁的方法,我们此处可以对伤处库存查询于库存扣减操作加锁来处理,但是加锁也要注意防止锁释放了其他线程获取到了锁,但是之前的事务还没有提交,这又会产生新的问题,下面会提到。

2.乐观锁

版本号:

乐观锁的方式我们可以通过给库存来加一个版本号,当线程1与线程2同时读取到库存都为1时,此时版本号也为1,线程1进行了扣减库存操作修改数据库时,加版本号判断的条件:数据库里版本号与上面查询库存时的版本号相同则修改成功,此时线程1修改时数据库中版本号为1,上面查询库存时版本号也为1则修改成功,这个时候线程2也进行扣减库存操作,当他去修改数据库时,发现他在查询库存时的版本号为1,但是数据库中由于线程1已经修改版本号为2,此时线程2则不能扣减成功。

 

CAS: 

我们也可以采用CAS的办法,线程1与2查询库存都为1,此时线程1进行后续操作扣减库存时进行判断,看查询库存时的库存数量与数据库中的库存数量是否相同,如果相同则进行扣减,线程1查询库存为1,修改时数据库中库存也为1,此时线程1扣减成功,这个时候线程2再去进行扣减库存时发现之前查询库存时的库存为1,但是扣减库存时数据库的库存为0,则扣减失败

 

4、新的问题

上述解决办法虽然解决了超卖的问题但同时又带来了新的问题,加入此时库存还有100,两个线程去进行购买操作时,都读取到库存为100,线程1进行后续扣减操做时发现之前查询的库存100与数据库中100相同 where 库存=100成立,于是扣减成功,下单成功。此时线程2再去执行扣减操作时,发现数据库中库存变为99但是查询时却是100,于是where 库存=100不成立,则产生了库存足够但不同下单的问题

5、解决

此时我们只需要在扣减库存时将原来的where 库存=查询时库存,改为where 库存>0,此时就既可以解决超卖问题,又能解决上述问题

二、一人一单

1、背景

很多时候有些商家要拿出一部分好用且贵重的产品来做促销引流,而将该物品进行低价售出,此时为了防止有人恶意低买高卖以及保证引流的效果,我们要保证一个用户只能买一次,也就一人一单

2、产生原因以及线程安全问题

一人一单的实现步骤是在原来下单逻辑的判断库存是否足够之后去查询数据库看是否该用户的是否已经存在订单,如果存在则不能下单成功,如果不存在则继续下单。由于该物品的特殊性,当开始秒杀时的并发量是极高的,这就会产生这样的问题,多个线程查询库存后判断该用户是否存已经存在订单时,此时这些线程都没有查询到订单信息说明该用户未下过单,让这些线程进行后续下单操作,但是在查询后有线程下单成功了,但是其他线程已经判断为未下过单,还在进行后续的下单操作,这就导致一个用户下单好几次

 

3、解决

我们可以对上述查询是否下过单与后续的库存扣减操作进行加锁,那么加锁是单纯给方法加吗?如果给方法加给一个用户的线程进来时都需要进行锁竞争,由于这是一个被高并发访问的这就会导致用户响应慢体验差,很多线程进入阻塞等待,所以单纯的给方法加锁是不行的。上述线程安全问题是一个用户的不同线程来并发访问产生的,所以我们可以对用户进行加锁,不同的用户线程是不需要去竞争锁的这样极大的提高了响应速度,我们可以将查询是否下过单与扣减库存封装成一个方法

@Transactionl
public void createVoucherOrder(Long userId) {
    synchronized(userId.toString().intern()){
        // 查询是否已经下单

        // 扣减库存
    }

}

在针对用户加锁时,我们需要将用户id转为字符串并存入字符串常量池中,如果不存在字符串常量池中则每次用户的Id在堆中的内存不同则不能达到针对用户加锁的效果 

4、新的问题----集群下的并发安全问题

上述加速在很大程度上保证了一人一单的线程安全问题,但是还有一种情况,就是上述我们提到的线程1进行操作后把锁释放了,但是事务还没提交也就是数据库中已下单还没有订单信息,此时线程2获取到了锁,查询数据库中是否存在订单信息时没有查询到,于是又去进行了扣减库存操作,此时该用户一个人又下了多单

5、解决

上述问题的产生原因是锁释放在事务提交之前,所以我们要做的就是保证锁释放在事务提交之后,那么怎么保证呢?我们可以将加锁的位置进行修改,在调用该方法的地方进行加锁即可

public void test(Long userId) {

    // 查询库存判断是否足够

    // 查询是否已下单与扣减库存
    synchronized(userId.toString().intern()){
        createVoucherOrder
    }
}



@Transactionl
public void createVoucherOrder(Long userId) {
    // 查询是否已经下单

     // 扣减库存

}

三、集群下的并发问题

1、说明

上述悲观锁解决思路可以解决单机环境下的线程安全问题,但是在集群模式下就不行了,在不同的服务器进行部署该服务时,由于不同服务器有着不同的JVM,其线程锁的监视器也不同,所以加锁不能解决集群环境下的安全问题

2、解决

针对上述集群环境我们可以采用分布式锁的解决方案,在后续的文章会提到。

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

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

相关文章

如何将ChatGPT训练成某个领域的专家!附完整示例!

背景 最近听了 八叉的一个ChatGPT讲座,讲的是如何将ChatGPT训练成领域专家,这样我们就可以用它来解决该领域的各种问题。 整个讲座中最让我印象深刻的就是训练方法,它是通过让ChatGPT向我们提问,然后由我们给出答案的方式进行训…

牛客 BM40 重建二叉树

描述 给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示。 提示: 1.vin.length pre.length 2.pre 和 vin 均无重复元素…

Redis未授权漏洞复现

Redis简介 Redis是C语言开发的一个开源高性能(key-value)键值对类型的内存NoSQL数据库,可以用作数据库、缓存、信息中间件(性能非常优秀,支持持久化到硬盘且高可用)。由于其自身特点,可以广泛应用在数据集群&#xff…

threeJs进阶 让模型沿着指定轨迹移动与转向

效果图: 涉及相关知识点: 欧拉对象和四元数主要用来表达对象的旋转信息。 关键词:欧拉Euler、四元数Quaternion、矩阵Matrix4 欧拉对象Euler 欧拉角描述一个旋转变换,通过指定轴顺序和其各个轴向上的指定旋转角度来旋转一个物体…

小红书达人等级有哪些,达人种草力度判断

小红书对于产品及品牌的传播作用,来自于达人自身的分享。以笔记为媒介,对产品进行情景化展示,从而吸引消费,就被称作是种草。而种草力度的强弱,则与达人等级息息相关。下面,就来跟详细为大家解读。 一、小红…

VHDL的基本语法(一)

1 VHDL基本结构 1 实体 Entity:描述所设计的系统的外部接口信号,定义电路设计中所有的输入和输出端口 2 结构体 Architecture:描述系统内部的结构和行为 3 包集合 package:存放各设模块能共享的数据类型、常数和子程序等&#xf…

一百一十、Hive时间转换——from_unixtime踩坑(不要用from_unixtime,而是用from_utc_timestamp)

1.详情 从kettle转换任务得到时间戳为13位,1683701579457。想看看这个时间戳与createTime字段的关系,于是一开始使用了from_unixtime,结果踩坑了 2.运行问题(晚8个小时) hive> select from_unixtime(cast(1683701…

产品经理怎样活着走出需求评审会?

需求评审是产品经理工作的重要环节,是团队成员间衔接需求的重要桥梁,产品经理的方案能准确落地的重要保障。 一场成功的需求评审会,是能够完整清晰传递产品目标、产品功能,能获得团队认同,并且会后团队能够配合实施的…

orin配置系统

查看linux下的opencv安装版本: pkg-config --modversion opencv查看linux下的opencv安装路径: sudo find / -iname "*opencv*"可知opencv安装在/usr/local/lib里面。 在~/.bashrc中配置如下 在刷机完成的Orin,执行如下命令以安装…

uboot下内存操作mw和md命令详解

mw简介 u-boot 中的 mw 命令是用于向内存写入数据的命令,它有4种形式: mw.b - 写入 1 个字节(8 比特)的数据mw.w - 写入 1 个字(2 字节,16 比特)的数据mw.l - 写入 1 个长字(4 字节,32 比特)的数据mw.q - 写入 1 个四字(8 字节,64 比特)的数据 它们的语法格式是: mw.b addres…

servlet的运行原理

Servlet在容器中的执行过程 1.浏览器向服务器发出GET请求 2.服务器上的Tomcat接收到该url,根据该url判断为Servlet请求,此时Tomcat将产生两个对象:请求对象(HttpServletRequest)和响应对象(HttpServletResponce) 3.Tomcat根据url找到目标Servlet,且创建…

kubernetes详细介绍

kubernetes组件 1 kubernetes组件2 kubernetes概念3 Pod3.1 pod的生命周期1. Pod会出现5种状态2. pod的创建过程3. pod的终止过程 3.2 Pod控制器1. 什么是Pod控制器2. ReplicaSet(RS)3. Deployment4. Horizontal Pod Autoscaler(HPA)5. DaemonSet6. Job7. Cronjob 4 Service4.1…

TypeScript extends和implements区别

(被人理解是幸运的,但不被理解未必不幸。一个把自己的价值完全寄托于他人的理解上面的人往往并无价值。——周国平) extends 相关文章 implements implements是一个类实现一个接口用的关键字.实现一个接口,必须实现接口中的所有…

React+Antd+Vite+TypeScript 项目实战教程(一)

本教程属于react入门教程,课程围绕如何搭建一个项目框架展开,会带你快速了解react、redux、redux-devtool、react-router-dom、axiox这些常见技术的使用方式,教程最后会附上项目源码。 一、创建项目 在搭建项目时,我们通常会使用…

QT界面开发杂记(五)

QString转char* QString("name").toStdString().c_str() c_str()没有‘\0’结尾可能导致一些错误可以使用以下方法解决: QString xmlPath "path"; const char cXmlName[1024] {0}; memcpy((void*)cXmlName,xmlPath.toStdStri…

目标检测 pytorch复现CenterNet目标检测项目

目标检测 pytorch复现CenterNet目标检测项目 1、项目创新点2、CenterNet网络结构3、CenterNet的模型计算流程如下:4、详细实现原理4.1、heatmap(热力图)理解和生成4.1.1 heatmap生成4.1.2 heatmap高斯函数半径的确定 4.1.3 CenterNet中生成高斯核的部分代码进行解析…

关于hashmap,希望能够帮到你

文章目录 前言介绍hashmap前先说一下关于的map知识 一、Map的概念和场景1.map的概念2.模型1. 纯 key 模型2. Key-Value 模型 二、Map的使用1.关于Map的使用2. 关于Map.Entry<K, V>的说明3. Map 的常用方法说明 三.hashmap1.方法构造2 冲突-概念3. 冲突-避免-哈希函数设计…

深入学习MYSQL-数据检索

前言 由于大部分基础知识都已经学过了&#xff0c;这里只把觉得应该记录一下的知识点做个笔记。然后以下笔记和sql均来自书籍(MYSQL必会知识) LIKE模糊查询 通配符% 相当于是查询一jet开头后面任意的数据 select prod_id,prod_name from products where prod_name like jet…

GRPC 程序在 Kubernetes 中的负载均衡

本文的背景使用的是 kratos 框架。 背景 众所周知 grpc 底层使用 http2 协议&#xff0c;而 http2 是一个长链接多路复用的。在正常情况下客服端与服务端一对一不会需要负载均衡手段&#xff1b;但是当服务上云之后为了保障服务的可用性所以我们服务端一般是多副本&#xff0…

用chatgpt实现 java导出excel复杂表。

记录一次使用chatgpt解决实际问题的&#xff0c;需求是在页面添加一个订单导出excel的功能&#xff0c;订单编号、订单明细&#xff0c;相同订单编号合并单元格&#xff0c;模板如下 表头表尾不用说&#xff0c; 主要是表格内容部分&#xff0c;左边是订单编号&#xff0c;右边…