Java高并发秒杀系统(二)
来源:互联网 发布:hadoop书籍推荐知乎 编辑:程序博客网 时间:2024/06/05 19:41
- 秒杀系优化分析
- 1 详情页面
- 2系统时间
- 3地址暴露接口
- 4执行秒杀操作
- 秒杀系统优化实现
- 1 秒杀地址接口优化
- 秒杀系统的部署
- 1 系统用到的服务
- 2请求处理的流程
- 秒杀系统优化总结
- 资源地址
本文主要对秒杀系统在大并发的场景下性能瓶颈的做一个分析,以及秒杀系统的优化实现。秒杀系统的业务分析和系统实现,可以参考上一篇文章 Java高并发秒杀系统(一)
1.秒杀系优化分析
下图列出秒杀的系统流程,其中红色部分是可能发生高并发的地方,绿色则表示没有影响。
1.1 详情页面
详情页面放在CDN
秒杀还未开始,大量的用户不断的点击刷新页面。我们将详情页静态化放到CDN,这样访问detail页面就不会落到我们的业务系统,可以减轻系统的压力。CDN是什么?
CDN是Content Delivery Network(内容分发网络)的缩写,是可以加快用户获取数据的系统。一般将一些不经常变化的资源放到CDN,比如:静态资源(图片、HTML、CSS、JavaScript)、视频文件等,用户访问到CDN上的资源后就不用请求应用系统,不但减轻了系统的压力还可以提升用户的访问速度。
1.2系统时间
为什么单独提供获取系统时间接口?
系统部署的时候会将秒杀详情页面放到CDN,用户访问页面的时候不用访问我们的系统,所以也就拿不到系统时间,因此提供一个获取服务器系统时间的接口。
获取系统时间接口不需要优化
获取系统时间接口不需要优化,是因为这个接口操作是通过访问内存实现的,访问一次内存大约花费10ns。内存操作速度很快,接口只有一个 new Date() ,基本上可以不用考虑GC,1s可以执行10亿次操作。
1.3地址暴露接口
能否放到CDN缓存?
秒杀地址接口无法使用CDN服务,因为CDN适用于请求资源不会变化的。而秒杀地址接口返回的数据会发生变化的,因此不适合放在CDN缓存。
秒杀地址接口优化思路
可以考虑将秒杀暴露接口返回的结果放到redis中,设置过期时间保证数据的一致性,或者可以在数据发生变化时通知redis将相应的数据进行更新。
1.4执行秒杀操作
秒杀其他方案分析
1)通过原子计数器记录商品的库存,一般采用redis或者其他NoSQL来保证库存数的原子性。2)记录成功后,将购买记录的行为消息发送到分布式消息队列中。3)后端系统从消息队列中消息消息,将相应数据修改落地到MySQL4)优点:方便扩展、伸缩性好,能够抗住非常高的并发。
大型的互联网公司基本都采取这种方案。
使用这套方案的成本非常高,不管是运维成本还是开发成本,需要技术人员对这些技术有比较深入的了解。
如何判断秒杀操作成功?
秒杀操作瓶颈分析
秒杀对应数据库中就是减库存操作和插入购买记录这两步操作,数据库使用行级锁来保证操作的原子性,这也就导致秒杀变成了串行操作。
由于应用系统和MySQL数据库经常不是部署在同一台机器上,所以数据库操作的都要经过网络传输,这就产生了网络延迟问题,通过还伴随着GC操作。
1)网络延迟分析
下面分别对同城机房和异地机房进行分析:2)减少锁的持有时间
秒杀优化方案
1)简单的优化:将insert语句和update语句调换位置,先执行insert语句,可以去除一些重复秒杀减掉一半的网络延迟和GC,目的是降低MySQL的行rowLock时间。2)深度优化:将事务操作放到MySQL端执行(存储过程),减少网络延迟和GC的成本,实现方案有两种
2.秒杀系统优化实现
2.1 秒杀地址接口优化
- service层增加redis缓存
因为秒杀商品对象在秒杀活动期间一般不会发生变化的,所以可以在这里做一层缓存。先从缓存中获取秒杀商品对象,如果没有,则访问数据库拿到商品对象之后再放入redis中。如果有,则直接将秒杀地址返回。
public Exposer exportSeckillUrl(long seckillId) { //优化点1:缓存优化:超时的基础上维护一致性 //1.访问redis SecKill secKill = redisDao.getSeckill(seckillId); if(secKill == null){ //2.访问数据库 secKill = secKillDao.queryById(seckillId); if(secKill == null){ return new Exposer(false,seckillId); }else{ //3.放入redis redisDao.putSeckill(secKill); } } Date startTime = secKill.getStartTime(); Date endTime = secKill.getEndTime(); //系统当前时间 Date noewTime = new Date(); if(noewTime.getTime() < startTime.getTime() || noewTime.getTime() > endTime.getTime()){ return new Exposer(false,seckillId,noewTime.getTime(),startTime.getTime(),endTime.getTime()); } //转化特定字符串的过程,不可逆 String md5 = getMD5(seckillId); return new Exposer(true,md5,seckillId); }
- 将数据库操作放到MySQL端执行(存储过程)
创建存储过程,service层通过调用存储过程获取秒杀结果。存储过程定义如下,通过判断返回结果result值来判断,秒杀结果。
-- 秒杀执行存储过程DELIMITER $$ -- console ; 转换为 $$-- 定义存储过程-- 参数:IN 输入参数; OUT 输出参数-- row_count(): 返回上一条sql(delete,insert,update)的影响行数-- row_count:0:未修改数据; >0: 表示修改的行数; <0: sql错误/未执行修改sqlCREATE PROCEDURE `seckill`.`execute_seckill` (IN v_seckill_id BIGINT, IN v_phone BIGINT, IN v_kill_time TIMESTAMP,OUT r_result INT) BEGIN DECLARE insert_count INT DEFAULT 0; START TRANSACTION ; INSERT IGNORE INTO success_killed(seckill_id, user_phone, state, create_time) VALUES (v_seckill_id,v_phone,0,v_kill_time); SELECT row_count() INTO insert_count; IF (insert_count = 0 ) THEN ROLLBACK ; SET r_result = -1; ELSEIF (insert_count < 0) THEN ROLLBACK ; SET r_result = -2; ELSE UPDATE seckill SET number = number - 1 WHERE seckill_id = v_seckill_id AND end_time > v_kill_time AND start_time < v_kill_time AND number > 0 ; SELECT row_count() INTO insert_count; IF (insert_count = 0) THEN ROLLBACK ; SET r_result = 0; ELSEIF (insert_count < 0) THEN ROLLBACK ; SET r_result = -2; ELSE COMMIT ; SET r_result = 1; END IF; END IF ; END;$$-- 存储过程定义结束DELIMITER ;--SET @r_result = -3;-- 执行存储过程CALL execute_seckill(1003,18270919398,now(),@r_result);-- 获取结果SELECT @r_result;-- 存储过程-- 1.存储过程优化:事务行级锁持有时间-- 2.不要过度依赖存储过程-- 3.简单的逻辑可以应用存储过程-- 4.QPS:一个秒杀单 6000/QPS-- 查看存储过程定义:show create procedure execute_seckill\G
3.秒杀系统的部署
3.1 系统用到的服务
1. CDN:内容分发网络,加速用户获取数据,降低服务器请求量2. WebServer:Nginx做http服务器以及Jetty服务器的反向代理3. redis:缓存热点数据4. MySQL:通过事务保证秒杀的一致性和完整性
3.2请求处理的流程:
1. 逻辑集群是我们开发的部分
4.秒杀系统优化总结
优化点
1.静态页面使用CDN缓存,实现动静态数据分离。2.后端不经常变化的数据放入redis中进行缓存3.将事务操作移到MySQL端:MySQL本地执行主键SQL可以达到4w QPS ,Java执行也很快,瓶颈主要存在于网络延迟以及GC的停顿操作。这里可以用存储过程,解决网络延迟以及GC停顿操作带来的问题。
并发优化
5.资源地址
慕课网视频地址
慕课网-Java高并发秒杀API之高并发优化github
秒杀系统代码
- Java高并发秒杀系统(二)
- Java高并发秒杀系统(一)
- Java高并发秒杀系统API
- 高并发秒杀系统
- Java高并发秒杀API(二)之Service层
- JAVA高并发秒杀系统构建之——高并发优化分析
- 秒杀系统高并发优化
- 秒杀系统高并发优化
- 高并发秒杀系统的优化
- 高并发秒杀系统优化
- java 高并发操作实例-秒杀
- Java高并发(一)-- 秒杀
- #Java 高并发秒杀API 笔记
- Java 实现高并发秒杀
- Java高并发秒杀API
- Java高并发秒杀API之service层实现(二)
- java b2b2c多用户商城系统架构之第二篇——高并发、秒杀
- JAVA高并发秒杀系统构建之——业务分析与Dao层搭建
- 小程序的视频代码示例
- HDU_2009数列求和问题
- Jenkins安装
- dubbo-admin管理平台搭建
- iptables之tcp flags
- Java高并发秒杀系统(二)
- 不一样的中国风,不一样的中国元素
- python中的闭包以及对装饰器的理解
- 在vue中写一个echart 圆形图
- ES6新特性之解构、参数、模块和记号用法示例
- PHP八大设计模式
- 【LeetCode】136. Single Number
- selenium+python设置爬虫代理IP
- Java操作Hadoop详解