- 浏览: 108963 次
-
文章分类
最新评论
-
kaizi1992:
嗯嗯。是的。@cs6641468 谢谢!希望多提意见
spring boot框架学习之重要注解3注解方式读取外部资源配置文件 -
cs6641468:
1. Spring Boot引入文件配置,优先考虑推荐的@Co ...
spring boot框架学习之重要注解3注解方式读取外部资源配置文件
本篇,咱们来实现优惠券秒杀下单功能。通过本篇学习,我们将会有如下收获:
1:优惠券领券业务逻辑;
2:分析在高并发情况下,出现超卖问题产生的原因;
3:解决超卖问题两种方案:版本号法及CAS法
4:乐观锁弊端改进方案;
本文涉及内容比较多,篇幅会比较长,同时有大量截图。希望大家能耐心看完。好了,话不对说,咱们开始go go go~
一:基本的秒杀实现
下单时候需要判断:
1:秒杀是否开始或结束,如果尚未开始或者已经结束则无法下单;
2:库存是否充足,不充足无法下单
业务:
根据上图逻辑,我们可以得到代码相关逻辑:
1:查下优惠券、2:判断是否秒杀开始;3:判断秒杀是否结束;4:判断库存是否充足;5:扣减库存;6:创建订单;
相关代码如下:
二:分析上面代码是否存在问题
我们使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
异常是45.5%。这个不对啊,按照我们预期的应该是50%的用户失败才对。这45.5%,说明优惠券超卖出了9个。是吗?我们来查查优惠券表:
库存为-9.再来查询订单表:发现订单是109条。在高并发的情况下,还真的是超卖出了9个呢。
来分析为什么会出现这种情况呢?
来看看代码,扣减库存的相关代码:
我们来分享下扣除库存流程:
两个线程来抢,假设当前就库存就剩下一个了。线程1和线程2来抢这个库存。流程如下:
在高并发的情况下,线程谁先执行,还真不好说。在高并发情况下,可能执行的顺序就如下图:
超卖问题分析:
T1的时候,线程1执行从数据库查询操作,查询结果为1;然后CPU让出,线程2来执行,在T2时候,线程2也去执行数据库查询操作,查询结果也是1.然后线程2,让出CPU,T3时候,线程1得到了CPU执行权,执行扣除库存操作。T4时候线程得到了CPU执行权,同样执行扣除库存操作。当两个线程都执行完成后,数据库中的库存就成了-1了。
这只是有2个线程,当高并发的时候,有多个线程来查询库存,扣除库存。如果出现了上面情况,就会出现超卖情况。
超卖问题场景的解决方案
超卖问题就是典型的多线程安全问题,针对这一问题常见的解决方案就是加锁。锁分为乐观锁和悲观锁。我们来看看:
悲观锁:认为线程安全问题一定会发生的,因此在操作数据之前,先获取锁,确保线程串行执行。
例如:Synchronized、Lock都是悲观锁。
因为让线程串行了,所以,悲观锁的效率低。
乐观锁:认为线程安全问题不一定会发生,因此不加锁,只是在更新数据的时候,判断有没有其他线程对数据做了修改。
如果没有修改,则认为是安全的,自己才更新数据;
如果已经被其他线程修改了说明发生了安全问题,此时可以重试或者抛出异常。
乐观锁的关键是判断之前查询得到的数据是否被修改过,常见的方式有两种:
1:版本号法
每当数据被修改,版本号就+1
我们来看看还是上面多线程抢优惠券情况下,版本号法执行流程:
线程1,执行扣除库存后,版本号+1后,就是2。如下图:
我们再来看看线程2执行流程:
版本号法优化:
我们从上图的逻辑中可以看出,在查询库存的时候,同时把版本号也查询出来,在更新的时候,库存-1,版本号也-1.where条件是版本号=查询库存的时候的版本号。我们只需要观察版本号和库存关系:同时查询出来、同时-1.那么,我们可不可以优化下,只使用一个字段来实现呢?答案是可以的:我们就把库存作为版本号概念,在更新的时候,where 条件中的version=查询库存的时候的版本号这个条件换成:where id =10 and stock = #{stock}。这样就剩下一个字段。
其实,上面这个思路就是大名鼎鼎的CAS思想,也就是第二种常见的方案。
2:CAS法
我们来看看CAS法逻辑图:
知识小扩展:
针对CAS中自旋压力过大,我们可以使用Longadder这个类来解决。在Java8中提供了一个对AtomicLong改进的一个类:LongAdder.大量线程并发更新一个原子性的时候,天然的问题就是自旋,会导致并发性能问题,当然这个也比我们直接使用sync来得好。所以可以利用这个类,LongAdder来进行优化。
如果获取某个值,则会对cell和base值进行递增,最后返回一个完整的值。
好了,秒杀超卖问题分析完了,解决方案也有了。那么接下来,我们就来实现解决超卖问题的代码。
其实,我们只需要修改扣减库存的逻辑,只添加一个where条件即可。如下图:
修改完成之后,我们再使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
异常竟然是89.9%。比没修改前,异常率还增加了。我们再来看看结果树情况:
一上来,就库存不足了。我们z看看数据库中,库存情况:
优惠券领券了21张。为什么会出现这种情况呢?200个人来抢购100张优惠券,竟然才有21个人抢到了。这个肯定不是我们想要的结果。这个是什么原因导致的呢?其实这个就涉及到了CAS乐观锁的弊端了。我们重新分析:
如上图,假设刚开始,就有3个线程同时抢夺资源,其中线程3先执行了更新,将100更新成了99,然后线程1和线程2,就更新失败了。三个线程,只有一个更新成功了,就如同,我们在结果树上看到的一样。如下图:
那么失败的这两个,就抢不到了,导致我们库存有剩余。但是,咱们从真正的业务上来说,抢不到的依据是库存等于0,才算抢不到,而不是说我抢到之后,在修改的时候,别人不能够在抢成功了。我们线程1和线程2在抢的时候,库存还剩余99啊,这个是不符合实际业务的。这就是乐观锁方案的问题所在--成功率太低了。那么,我们对乐观锁法进行改进。
乐观锁法弊端改进
改进思路:在更新的时候,不再判断库存是否等于我手里的库存值。而是判断,库存是否大于0.如果大于,就执行扣除操作。
修改扣除库存相关代码:
修改完成之后,我们再使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
从上图中,我们看到异常率是50%。符合我们的预期。我们看看数据库中的库存:
订单表中也是100条订单。商品没有超卖,订单数量也正常。这样是不是很完美解决了超卖问题?
答案:否。我们可以看到,这个方案,直接是由数据库来处理的。我们知道,数据库本来就是比较宝贵的资源,在高并发情况下,这种方案,肯定是不行的。我们继续往下学习。
小总结
我们来总结下超卖这样线程安全问题,解决方案有哪些?
下一篇预告:
在下一篇中咱们将实现另外一个功能:一人一单的功能。在下一篇中,您将有如下收获:
1:悲观锁、乐观锁的使用场景;
2:synchronized关键字,在不同位置,锁的颗粒度是不同的,怎么优化呢;
3:toString方法之后,不能保证唯一,如果要保证唯一,需要在调用String的intern方法;
4:对spring事务有更深入了解-解决spring事务失效一种情况;
5:spring boot怎么开启对AspectJ的支持。
1:优惠券领券业务逻辑;
2:分析在高并发情况下,出现超卖问题产生的原因;
3:解决超卖问题两种方案:版本号法及CAS法
4:乐观锁弊端改进方案;
本文涉及内容比较多,篇幅会比较长,同时有大量截图。希望大家能耐心看完。好了,话不对说,咱们开始go go go~
一:基本的秒杀实现
下单时候需要判断:
1:秒杀是否开始或结束,如果尚未开始或者已经结束则无法下单;
2:库存是否充足,不充足无法下单
业务:
根据上图逻辑,我们可以得到代码相关逻辑:
1:查下优惠券、2:判断是否秒杀开始;3:判断秒杀是否结束;4:判断库存是否充足;5:扣减库存;6:创建订单;
相关代码如下:
二:分析上面代码是否存在问题
我们使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
异常是45.5%。这个不对啊,按照我们预期的应该是50%的用户失败才对。这45.5%,说明优惠券超卖出了9个。是吗?我们来查查优惠券表:
库存为-9.再来查询订单表:发现订单是109条。在高并发的情况下,还真的是超卖出了9个呢。
来分析为什么会出现这种情况呢?
来看看代码,扣减库存的相关代码:
我们来分享下扣除库存流程:
两个线程来抢,假设当前就库存就剩下一个了。线程1和线程2来抢这个库存。流程如下:
在高并发的情况下,线程谁先执行,还真不好说。在高并发情况下,可能执行的顺序就如下图:
超卖问题分析:
T1的时候,线程1执行从数据库查询操作,查询结果为1;然后CPU让出,线程2来执行,在T2时候,线程2也去执行数据库查询操作,查询结果也是1.然后线程2,让出CPU,T3时候,线程1得到了CPU执行权,执行扣除库存操作。T4时候线程得到了CPU执行权,同样执行扣除库存操作。当两个线程都执行完成后,数据库中的库存就成了-1了。
这只是有2个线程,当高并发的时候,有多个线程来查询库存,扣除库存。如果出现了上面情况,就会出现超卖情况。
超卖问题场景的解决方案
超卖问题就是典型的多线程安全问题,针对这一问题常见的解决方案就是加锁。锁分为乐观锁和悲观锁。我们来看看:
悲观锁:认为线程安全问题一定会发生的,因此在操作数据之前,先获取锁,确保线程串行执行。
例如:Synchronized、Lock都是悲观锁。
因为让线程串行了,所以,悲观锁的效率低。
乐观锁:认为线程安全问题不一定会发生,因此不加锁,只是在更新数据的时候,判断有没有其他线程对数据做了修改。
如果没有修改,则认为是安全的,自己才更新数据;
如果已经被其他线程修改了说明发生了安全问题,此时可以重试或者抛出异常。
乐观锁的关键是判断之前查询得到的数据是否被修改过,常见的方式有两种:
1:版本号法
每当数据被修改,版本号就+1
我们来看看还是上面多线程抢优惠券情况下,版本号法执行流程:
线程1,执行扣除库存后,版本号+1后,就是2。如下图:
我们再来看看线程2执行流程:
版本号法优化:
我们从上图的逻辑中可以看出,在查询库存的时候,同时把版本号也查询出来,在更新的时候,库存-1,版本号也-1.where条件是版本号=查询库存的时候的版本号。我们只需要观察版本号和库存关系:同时查询出来、同时-1.那么,我们可不可以优化下,只使用一个字段来实现呢?答案是可以的:我们就把库存作为版本号概念,在更新的时候,where 条件中的version=查询库存的时候的版本号这个条件换成:where id =10 and stock = #{stock}。这样就剩下一个字段。
其实,上面这个思路就是大名鼎鼎的CAS思想,也就是第二种常见的方案。
2:CAS法
我们来看看CAS法逻辑图:
知识小扩展:
针对CAS中自旋压力过大,我们可以使用Longadder这个类来解决。在Java8中提供了一个对AtomicLong改进的一个类:LongAdder.大量线程并发更新一个原子性的时候,天然的问题就是自旋,会导致并发性能问题,当然这个也比我们直接使用sync来得好。所以可以利用这个类,LongAdder来进行优化。
如果获取某个值,则会对cell和base值进行递增,最后返回一个完整的值。
好了,秒杀超卖问题分析完了,解决方案也有了。那么接下来,我们就来实现解决超卖问题的代码。
其实,我们只需要修改扣减库存的逻辑,只添加一个where条件即可。如下图:
修改完成之后,我们再使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
异常竟然是89.9%。比没修改前,异常率还增加了。我们再来看看结果树情况:
一上来,就库存不足了。我们z看看数据库中,库存情况:
优惠券领券了21张。为什么会出现这种情况呢?200个人来抢购100张优惠券,竟然才有21个人抢到了。这个肯定不是我们想要的结果。这个是什么原因导致的呢?其实这个就涉及到了CAS乐观锁的弊端了。我们重新分析:
如上图,假设刚开始,就有3个线程同时抢夺资源,其中线程3先执行了更新,将100更新成了99,然后线程1和线程2,就更新失败了。三个线程,只有一个更新成功了,就如同,我们在结果树上看到的一样。如下图:
那么失败的这两个,就抢不到了,导致我们库存有剩余。但是,咱们从真正的业务上来说,抢不到的依据是库存等于0,才算抢不到,而不是说我抢到之后,在修改的时候,别人不能够在抢成功了。我们线程1和线程2在抢的时候,库存还剩余99啊,这个是不符合实际业务的。这就是乐观锁方案的问题所在--成功率太低了。那么,我们对乐观锁法进行改进。
乐观锁法弊端改进
改进思路:在更新的时候,不再判断库存是否等于我手里的库存值。而是判断,库存是否大于0.如果大于,就执行扣除操作。
修改扣除库存相关代码:
修改完成之后,我们再使用JMeter模拟200个用户去秒杀抢优惠券。运行结果:
从上图中,我们看到异常率是50%。符合我们的预期。我们看看数据库中的库存:
订单表中也是100条订单。商品没有超卖,订单数量也正常。这样是不是很完美解决了超卖问题?
答案:否。我们可以看到,这个方案,直接是由数据库来处理的。我们知道,数据库本来就是比较宝贵的资源,在高并发情况下,这种方案,肯定是不行的。我们继续往下学习。
小总结
我们来总结下超卖这样线程安全问题,解决方案有哪些?
下一篇预告:
在下一篇中咱们将实现另外一个功能:一人一单的功能。在下一篇中,您将有如下收获:
1:悲观锁、乐观锁的使用场景;
2:synchronized关键字,在不同位置,锁的颗粒度是不同的,怎么优化呢;
3:toString方法之后,不能保证唯一,如果要保证唯一,需要在调用String的intern方法;
4:对spring事务有更深入了解-解决spring事务失效一种情况;
5:spring boot怎么开启对AspectJ的支持。
发表评论
-
【架构设计】多级缓存:应用案例与问题解决策略
2024-09-17 09:23 217【架构设计】多级缓存:应用案例与问题解决策略 ... -
Nginx使用篇:实现负载均衡、限流与动静分离
2024-09-16 09:23 276Nginx使用篇:实现负 ... -
怎么在Windows操作系统部署阿里开源版通义千问(Qwen2)
2024-09-01 22:18 768怎么在Windows操作系统部署阿里开源版通义千问(Qwen ... -
Redis实战-session共享之修改登录拦截器
2023-02-11 15:36 471在上一篇中Redis实战之session共享,我们知道了通过 ... -
Redis实战之session共享
2023-02-06 14:24 582当线上集群时候,会出现session共享问题。 当线上集群 ... -
分库分表后全局唯一ID的四种生成策略对比
2023-02-02 09:00 493分库分表之后,ID主键如何处理? 当业务量大的时候,数据库 ... -
Redis快速入门
2023-01-31 08:30 514Redis快速入门,分两个客户端:Jedis和SpringD ... -
Redis实战9-全局唯一ID
2023-01-29 09:45 592发布优惠券的时候,每个店铺都可以发布优惠券,当用户抢购的时候 ... -
【图文教程】Centos单机安装Redis
2023-01-13 21:49 7271.1.安装Redis依赖 Redis是基于C语言编写 ... -
docker系列教程:docker图形化工具安装及docker系列教程总结
2022-12-31 21:00 698通过前面的学习,我们已经掌握了docker-compose容 ... -
docker高级篇-docker-compose容器编排介绍及实战
2022-12-30 09:42 0Docker-compose是什么?能干嘛?解决了哪些痛点? ... -
Docker网络下-自定义网络实战
2022-12-29 10:02 571通过前面两篇的学习,我们对docker网络及四大网络类型都了 ... -
Docker网络下-自定义网络实战
2022-12-29 09:54 544通过前面两篇的学习,我们对docker网络及四大网络类型都了 ... -
Docker网络中篇-docker网络的四种类型
2022-12-28 09:51 559通过上一篇学习,我们对docker网络有了初步的了解。本篇, ... -
Docker网络上篇-网络介绍
2022-12-27 10:28 0通过前面的学习,我们已经可以把自己写的微服务项目通过dock ... -
docker高级篇:实战-自己开发的微服务怎么在docker上面运行?
2022-12-26 21:06 527通过前面的一系列学习,我们已经知道怎么制作dockerfil ... -
docker的虚悬镜像是什么?
2022-12-25 13:39 604虚悬镜像是什么? 答:仓库名、标签都是<none&g ... -
docker高级篇第三章-dockerfile案例之制作自己的centos镜像
2022-12-24 15:27 549在上一篇文章中《Dockerfile介绍及常用保留指令》,我 ... -
Dockerfile介绍及常用保留指令
2022-12-23 11:03 434从本文开始,咱们将介绍docker的另外一个技术点:dock ... -
Docker高级:Redis集群实战!4主4从缩容到3主3从,怎么处理?
2022-12-22 09:10 375在上一篇,我们学会了redis集群的扩容。从3主3从扩容到4 ...
相关推荐
6. **促销与优惠模块**:包括优惠券、满减、秒杀等活动,通过促销策略吸引用户购买。需要处理复杂的计算逻辑,如优惠券的使用条件和有效期。 7. **支付与结算模块**:处理用户的支付请求,与支付网关交互,完成交易...
基于改进粒子群算法的DG储能选址定容优化模型:解决电力系统时序性问题的可靠程序解决方案,基于改进粒子群算法的DG储能选址定容模型优化解决电力系统问题,DG储能选址定容模型matlab 程序采用改进粒子群算法,考虑时序性得到分布式和储能的选址定容模型,程序运行可靠 这段程序是一个改进的粒子群算法,主要用于解决电力系统中的优化问题。下面我将对程序进行详细分析。 首先,程序开始时加载了一些数据文件,包括gfjl、fljl、fhjl1、cjgs和fhbl。这些文件可能包含了电力系统的各种参数和数据。 接下来是一些参数的设置,包括三种蓄电池的参数矩阵、迭代次数、种群大小、速度更新参数、惯性权重、储能动作策略和限制条件等。 然后,程序进行了一些初始化操作,包括初始化种群、速度和适应度等。 接下来是主要的迭代过程。程序使用粒子群算法的思想,通过更新粒子的位置和速度来寻找最优解。在每次迭代中,程序计算了每个粒子的适应度,并更新个体最佳位置和全局最佳位置。 在每次迭代中,程序还进行了一些额外的计算,如潮流计算、储能约束等。这些计算可能涉及到电力系统的潮流计算、功率平衡等知识点。 最后,程序输
数学建模相关主题资源2
内容概要:本文详细介绍了一系列用于科学研究、工程项目和技术开发中至关重要的实验程序编写与文档报告撰写的资源和工具。从代码托管平台(GitHub/GitLab/Kaggle/CodeOcean)到云端计算环境(Colab),以及多种类型的编辑器(LaTeX/Microsoft Word/Overleaf/Typora),还有涵盖整个研究周期的各种辅助工具:如可视化工具(Tableau)、数据分析平台(R/Pandas)、项目管理工具(Trello/Jira)、数据管理和伦理审核支持(Figshare/IRB等),最后提供了典型报告的具体结构指导及其范本实例链接(arXiv/PubMed)。这为实验流程中的各个环节提供了系统的解决方案,极大地提高了工作的效率。 适合人群:高校学生、科研工作者、工程技术人员以及从事学术写作的人员,无论是新手入门还是有一定经验的人士都能从中受益。 使用场景及目标:帮助读者高效地准备并开展实验研究活动;促进团队间协作交流;规范研究报告的形式;提高对所收集资料的安全性和隐私保护意识;确保遵循国际公认的伦理准则进行实验。
四轮毂驱动电动汽车稳定性控制策略:基于滑模与模糊神经网络的转矩分配与仿真研究,四轮毂驱动电动汽车稳定性控制:基于滑模与模糊神经网络的转矩分配策略及联合仿真验证,四轮毂驱动电动汽车稳定性控制,分布式驱动转矩分配。 上层基于滑模,模糊神经网络控制器决策横摆力矩,下层基于动态载荷分配,最优分配,平均分配均可做。 simulink与carsim联合仿真。 ,四轮毂驱动;电动汽车稳定性控制;分布式驱动;转矩分配;滑模控制;模糊神经网络控制器;横摆力矩;动态载荷分配;最优分配;平均分配;Simulink仿真;Carsim仿真,四驱电动稳定性控制:滑模与模糊神经网络决策的转矩分配研究
本资源提供了一份详细的PyCharm安装教程,涵盖下载、安装、配置、激活及使用步骤,适合新手快速搭建Python开发环境。
毕业设计
原版宋体.ttf,原版宋体安装文件,安装方式,直接右键安装。
利用Xilinx FPGA内嵌的软核处理器MicroBlaze,加上自主编写的AXI_IIC控制器,实现对IMX327传感器IIC总线的控制,同时辅以UART调试串口,实现系统状态的实时监控与调试。
在 GEE(Google Earth Engine)中,XEE 包是一个用于处理和分析地理空间数据的工具。以下是对 GEE 中 XEE 包的具体介绍: 主要特性 地理数据处理:提供强大的函数和工具,用于处理遥感影像和其他地理空间数据。 高效计算:利用云计算能力,支持大规模数据集的快速处理。 可视化:内置可视化工具,方便用户查看和分析数据。 集成性:可以与其他 GEE API 和工具无缝集成,支持多种数据源。 适用场景 环境监测:用于监测森林砍伐、城市扩展、水体变化等环境问题。 农业分析:分析作物生长、土地利用变化等农业相关数据。 气候研究:研究气候变化对生态系统和人类活动的影响。
毕业设计
整个文件的代码
名字微控制器_STM32_DFU_引导加载程序_dapboo_1740989527.zip
详细介绍及样例数据:https://blog.csdn.net/T0620514/article/details/145991332
anaconda配置pytorch环境
立体仓库控制组态王6.55与三菱PLC联机仿真程序:视频教程与IO表接线图CAD详解,9仓位立体仓库控制系统优化方案:组态王6.55与三菱PLC联机仿真程序视频教程及IO表接线图CAD详解,9仓位立体仓库控制组态王6.55和三菱PLC联机仿真程序+视频+带io表接线图CAD ,关键词:立体仓库;控制组态王6.55;三菱PLC;联机仿真程序;视频;io表接线图;CAD,立体仓库控制组态王与三菱PLC联机仿真程序资源包
基于Maxwwell设计的经典外转子永磁同步电机案例:直流母线24V,大功率与高效率驱动设计,基于Maxwell设计的经典永磁同步电机案例:200W功率,外转子结构,直流母线电压与电机参数详解,基于maxwwell设计的经典200W,2200RPM 外转子,直流母线24V,42极36槽,定子外径81.5 轴向长度15 ,0.86Nm, 永磁同步电机(PMSM)设计案例,该案例可用于生产,或者学习用 ,经典设计案例; 200W; 2200RPM外转子; 直流母线24V; 42极36槽; 定子外径81.5; 轴向长度15; 永磁同步电机(PMSM); 生产学习用。,经典200W永磁同步电机设计案例:Maxwell外转子,高效率2200RPM直流母线系统
C# Modbus RTU协议主站设计工程源码详解:支持多从站访问与多线程实现,带注释开源dll文件,C# Modbus RTU协议主站设计工程源码解析:多线程实现访问多个从站功能的开源dll文件,C# Modbus RTU协议主站设计工程源码带注释,开源dll文件,支持访问多个从站,多线程实现 ,C#; Modbus RTU协议; 主站设计; 工程源码; 注释; 开源dll; 多从站访问; 多线程实现,《C# Modbus RTU主站源码:多线程支持访问多从站开源DLL文件详解》
MATLAB Simulink下的四旋翼无人机PID控制仿真模型研究,MATLAB Simulink下的四旋翼无人机PID控制仿真模型研究,MATLAB Simulink 四旋翼仿真模型 四轴无人机PID控制 ,MATLAB; Simulink; 四旋翼仿真模型; 四轴无人机; PID控制,MATLAB Simulink四旋翼仿真模型中四轴无人机的PID控制研究