JAVA语言高并发秒杀系统构建之高并发优化分析
小标 2018-08-30 来源 : 阅读 536 评论 0

摘要:本文主要向大家介绍了JAVA语言高并发秒杀系统构建之高并发优化分析,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言高并发秒杀系统构建之高并发优化分析,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

先来分析一下java 控制事务行为

如下图可知,java事务是串联发生的,即当一个事务还没有执行完,其他事务都是要等待而被阻塞。

那一个事务具体要干什么东西呢?或者说高并发的瓶颈在哪里,看下图?

分析:
1、一个Java事务可能有多条sql语句的操作,此时算上sql语句操作时间。?
2、网络会出现延迟。?
3、GC(java回收机制)处理时,会消耗一定的时间?
综上,Java事务处理时间=sql消耗+网络延迟+GC消耗,一个java事务处理时间大约在2ms,也就是说1秒只能有500个事务串行发生,这是非常不高效的。

 

而之前说的行级锁(保证java事务之间可以串行进行,从而保证数据的一致性),则在commit之后就会释放,所以我们的优化方向,就是减少行级锁的持有时间

我们先分析如何判断update语句成功更新?
1、update语句自身没有报错?
2、客户端确认update影响记录数?
所以我们在这个方面的优化方案时,把客户端逻辑放到mysql服务端,避免网络延迟和GC?
即为,使用存储过程:整个事务在Mysql端进行,因为这样可以将网络延迟和GC消耗降至最低,大大减少了整个事务的运行时间,同时,Mysql一秒中可以做出几万次的事务,是非常高效快速的。

 

下面开始秒杀的实际优化

首先优化的应该是秒杀地址暴露接口,这里说下为啥要优化它?
1.因为秒杀地址暴露接口是根据后台判断秒杀开始时间,结束时间,以及当前时间来给客户端返回秒杀地址的,所以无法放进CDN中。?
2.秒杀地址暴露是在秒杀执行之前需要进行的动作,因此伴有大量的并发操作,同时需要请求数据库,因此必须要优化。?
3.针对这些情况,我们可以用redis,优化秒杀地址暴露接口。

首先建立一个redisDao类,专门进行与Redis有关的业务逻辑?

 

这里需要声明一下,如果从redis中获得数据,是获得byte的数组,而不是具体的类,因为redis无法帮你反序列化,因此,反序列化的操作应该由你自己完成。

因为java内置的序列化接口Serializable,效率没有protostuff自定义序列化高,protostuff的反序列化效率比Serializable高了很多倍,而且压缩后的空间比Serializable小了10倍左右,因此,使用protostuff进行自定义反序列化操作。

这里生成protostuff的一个schem,其原理是将Seckill.class的字符码传过去,然后利用反射的原理,获得Seckill中的属性值等。

接下来,通过redis获得缓存中的Seckill对象,以及一些protostuff的反序列化操作


接下来是将Seckill对象放进redis缓存中,这里同样利用protostuff来序列化Seckill,LinkedfBuffer为缓冲器,保证在大型类序列化时能够起到缓冲的作用,timeout设置缓存时间,这里设置一小时之后redis中的Seckill数据就会消失。result为redis返回的结果,可以依据此判断缓存是否成功。

这两个方法写好后,我们回到SeckillServiceImpl中,将之前Seckill直接从数据库中获取的流程,改成先判断redis里面有没有Seckill缓存,如果有,直接从redis中获取,如果没有,从数据库中取出Seckill,将其放入redis中。这里运用在超时的基础上维护redis数据的一致性,是最基础的维护一致性的方式。

以上就是通过redis来缓存信息,下面我们来看执行秒杀操作时的并发优化

上面我们总结过,行级锁的主要发生地,主要是库存update操作这里,之前的业务逻辑如下图
由下图可知,无论这个商品是否可以被秒杀,都会执行update语句,获得行级锁,再返回是否update成功的结果,并通过这个update操作结果,来插入购买明细,这是非常错误的。
我们知道,insert操作也可以判断是否插入成功(返回值0代表失败,1代表成功),但是,insert操作是不用通过行级锁来阻塞其他事务的,因此,我们可以将update语句和insert语句调换位置(先过滤不符合update操作条件的事务,即挡住一部分重复秒杀),通过insert语句的结果,来判断是否需要update操作,这样,阻塞时间将会大大减少,增加了系统的性能。

下图是调换位置后的事务执行

下面来看看具体代码的修改

 

接下来是深度优化,即将事务SQL在Mysql端进行(存储过程),减少网络延迟和GC带来的时间消耗

下面将插入购买明细和更新库存操作,放在存储过程里面执行

这里简单说明一下存储过程:
1.存储过程主要优化事务行级锁持有时间
2.不要过度依赖存储过程?
3.简单的逻辑可以应用存储过程?
4.一秒可以接受4000个请求,这是基本足够的。

下面说说在java客户端是如何调用存储过程的

在SeckillDao中,使用存储过程执行秒杀

然后在seckillDao.xml中实现它

在SeckillService接口中定义一个方法,作为利用存储过程来实现事务的方法,其参数和executeSeckill方法一样

然后编写其实现类

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程