秒杀,抢购热卖商品高并发场景

2024-05-15 09:48

本文主要是介绍秒杀,抢购热卖商品高并发场景,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在秒杀,限时抢购这种大促销场景下,由于峰值流量较大,大量的并发读/写操作除了会导致后端的存储系统产生性能瓶颈外,还会出现传说中的超卖情况。什么是超卖呢?比如某商品的库存为1,此时用户A和用户B并发购买该商品,用户A提交订单后该商品的库存被修改为0,而此时用户B并不知道的情况下提交订单,该商品的库存再次被修改为-1这就是超卖现象。从项目大小本身的角度来说,有下面三种解决方案(MySQL数据库):

一 悲观锁(InnoDB行锁)

实现方法:

1.MySQL常用引擎有MYISAM和InnoDB,而InnoDB是mysql默认的引擎。MYISAM不支持行锁,而InnoDB支持行锁,所以首先要检查MySQL当前引擎是否是InnoDB。

2.每次从数据库表读取商品的时候都加上for update,如select * from xxxx for update,这样一来的话用户A在进行读操作时(还没释放),用户B就需要排队等,一直等到用户A释放才可以读取到进行下面的业务操作,这样可保证商品在没改变库存的情况被多个用户读取到。

缺点:大量的线程(用户)相互竞争InnoDB的行锁,并发越大时,等待的线程就越多,这会严重影响数据库TPS,导致RT线性上升,除了引发常见的数据库死锁外,最终还可能会引发系统出现雪崩。所以使用这种方案来解决的业务场景一定是提前知道了用户流量和评估好服务器性能并提前做过压力测试,之前笔者有项目业务场景是提前用户注册进来,在正式抢购中限制了只能多少人来参加,所以使用这种方案也可以解决当时情景。

注意:有很多人经常反映说按照此方式去操作锁没生效或者有其它问题,那就要看下自己的整体业务逻辑代码是否有问题了,一般我们去抢够商品的时候,是在一个方法里面,如果这个方法名是:buy(),我们进来的时候第一行代码就要开始加锁for update,然后中间去处理业务逻辑并修改商品库存update,最后记得提交commit,这样的话,后面的用户都在等待前面的commit,才能进去往下走。

比如用户A进来

先开启一个事务

begin;

select stock from good where id=1 for update;//查询good表某个商品中stock的数量

查出来后,在程序里在判断这个stock是否足够

最后再执行

update good set stock=stock-1 where id=1

最后在

commit

但是这个时候用户B也是select stock from good where id=1 for update,这个时候会出现被锁住,无法被读取。

二 乐观锁(版本控制)

简单的来说,就是在商品表中建立一个version字段,在高并发场景下,必然会导致多个用户拿到的stock和version版本一样,不过没关系,当用户A成功扣减商品库存后,需要将商品表中的version加1,当用户B再去扣减库存时,由于version不匹配,操作无效,为了提升库存扣减成功率,也可以适当进行重试,如果库存不足,则说明商品已经售完,反之扣减库存后version继续加1。

比如用户A进来

select stock,version from good where id=1 //首先去拿到库存和版本,比如当到当前版本是2

查出来后,在程序里在判断这个stock是否足够

最后再执行

update good set version=version+1,stock=stock-1 where id=1 and version=2

如果有其它用户同时拿到了这个版本2,并比用户A提前修改,这个时候用户A操作是失败的

可以进行重试,再次去拿到库存和版本,后面一样的操作

 

除了使用乐观锁version字段,还可以更加简单,就用实际库存数>=扣减库存数作为条件来替换version版本即可,因为update本身就带锁,采用这种方式更加直接,也充分利用了InnoDB引擎的行锁特征,大大提高了库存扣减的成功率。

如update good set stock=stock-1 where id =1 and stock>=1

 

缺点:以上两种方式,在并发较大时,所有的请求都到底了数据层进行操作,等于把压力全部丢给了数据库,如果系统前端不配合做限流消峰等处理,数据库压力会非常高,代价就很昂贵,所以尽量把这一层的压力往上面转移。

三 在redis中扣减库存

redis的读写能力本身远胜mysql关系数据库,因此在redis中实现库存扣减是个不错的代替方案,这样数据库中的存储的商品库存可以理解为实际库存,而redis中存储的商品库存则为实时库存。

但是在并发较大的情况下,直接在redis中扣减库存也会导致商品出现超卖现象,因此必须引入分布式锁来避免超卖,关于分布式锁可以在我的物联网项目(八)简单分布式调度中查看。

大概实现方式是:数据库库存同步到redis中,当系统获取到分布式锁并成功扣减redis中的实时库存后,可以将消息写入到消息队列中,由消费者负责实际库存的扣减,由于采用了排队机制,并发写入数据库时的流量可控,因此数据库的负载压力保持在一个恒定的范围内,不会因为流量的影响而导致数据库性能下降,具体如图。

image.png

这篇关于秒杀,抢购热卖商品高并发场景的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991489

相关文章

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制