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

这篇具有很好参考价值的文章主要介绍了【Redis】电商项目秒杀问题之超卖问题与一人一单问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、超卖问题

1、背景

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

3、解决

1.悲观锁

2.乐观锁

4、新的问题

5、解决

二、一人一单

1、背景

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

3、解决

4、新的问题

5、解决

三、集群下的并发问题

1、说明

2、解决


一、超卖问题

1、背景

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

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

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

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

3、解决

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

1.悲观锁

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

2.乐观锁

版本号:

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

CAS: 

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

4、新的问题

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

5、解决

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

二、一人一单

1、背景

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

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

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

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、解决

针对上述集群环境我们可以采用分布式锁的解决方案,在后续的文章会提到。文章来源地址https://www.toymoban.com/news/detail-444757.html

到了这里,关于【Redis】电商项目秒杀问题之超卖问题与一人一单问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Redis】电商项目秒杀问题之下单接口优化:Redis缓存、MQ以及lua脚本优化高并发背景下的秒杀下单问题

    目录 一、优化思路 二、缓存库存与订单 1、库存缓存的redis数据结构 2、订单信息缓存的redis数据结构 三、整体流程 四、lua脚本确保权限校验操作的原子性 【Redis】电商项目秒杀问题之超卖问题与一人一单问题_1373i的博客-CSDN博客 https://blog.csdn.net/qq_61903414/article/details/1305689

    2024年02月05日
    浏览(31)
  • 并发安全问题之超卖问题

    并发安全问题之超卖问题 乐观锁总结: 优点 :不加锁性能好。 缺点 :同时请求成功率低(即只要发现数据变了就放弃了)。 乐观锁思想的具体体现 :一共两步,第一步,先查询状态。第二步,再更新数据时必须where等于前面的状态,确保数据没有改变。 第二步集查询和操

    2024年02月08日
    浏览(33)
  • 秒杀系统常见问题—库存超卖

    大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 以下是正文! 首先上一串代码 我们看一下这串代码,逻辑用流程图表示如下: 从图上看,逻辑还是很清晰明了

    2024年02月06日
    浏览(37)
  • 超卖等高并发秒杀场景的问题及解决方案

    多线程并行运行 多行代码操作共享资源,但不具备原子性 例: 针对并发安全问题,最广为人知的解决方案就是 加锁 。 从实现思想上来说,锁可以分为两大类: 悲观锁 乐观锁 悲观锁是一种独占和排他的锁机制,保守地认为数据会被其他事务修改,所以在整个数据处理过程

    2024年02月15日
    浏览(31)
  • 华为云应用中间件DCS系列—Redis实现(电商网站)秒杀抢购示例

    云服务、API、SDK,调试,查看,我都行 阅读短文您可以学习到:应用中间件系列之Redis实现(电商网站)秒杀抢购示例 华为云开发者插件(Huawei Cloud Toolkit),作为华为云围绕其产品能力向开发者桌面上的延伸,帮助开发者快速在本地连接华为云,打通华为云到开发者的最后

    2024年02月07日
    浏览(28)
  • 【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?

    一.多线程下引起的超卖问题呈现 1.1.我先初始化库存数量为1、订单数量为0 1.2.然后我开启3个线程去执行业务 业务为:判断如果说库存数量大于0,则库存减1,订单数量加1 结果为:库存为-2,订单数量为3 原因:如下图所示,这是因为分别有6个指令(3个库存减1指令,3个订单

    2024年02月03日
    浏览(43)
  • Redis项目实战——优惠券秒杀

    如果用MySQL的自增长ID,ID的规律性太明显, 会暴漏一些信息 (比如销量等) 数据量太大时一张表存不下,需要多张表,MySQL多张表的自增长都是独立的, 会出现重复ID 需要一种在分布式系统下可以生成全局唯一ID的工具,必须唯一且递增 在某项目里,不管数据库的表有多少

    2024年02月10日
    浏览(29)
  • 互联网中的商品超卖问题及其解决方案:Java中Redis结合UUID的应用

    在设计商品下单和库存扣减,你一定遇到过这样的问题,库存扣减为0了,可是消费者还能下单,并将订单信息保存到了数据库里,针对商品超卖问题,作此篇以解决。 随着互联网商业的飞速发展,商品超卖问题逐渐凸显为电商平台面临的一大挑战。尤其是在大型促销活动期

    2024年02月04日
    浏览(31)
  • Golang实现Redis分布式锁解决秒杀问题

    先写一个脚本sql,插入2000个用户 登录是通过2个字段,一个是mobile,一个是password,生成了mobile从1到2000,密码默认是123456 然后写一个单元测试,实现新注册的2000个用户登录,然后获取token 我们使用有缓冲的通道和sync.WaitGroup信号量,来控制协程的数量,经过测试,发现limi

    2024年02月14日
    浏览(32)
  • 微服务---Redis实用篇-黑马头条项目-优惠卷秒杀功能(使用java阻塞队列对秒杀进行异步优化)

    1.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求,此时会请求nginx,nginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一人一单 5、扣减库存 6、创建订单 在这六

    2024年02月05日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包