秒杀活动,提高性能,防止超卖,订单超时
目录
初步思考
秒杀活动
订单防止超卖
订单超时如何处理
初步思考
原文地址
前端:页面尽可能静态化,css/js合并,减少请求数
扩容:增加机器,提高处理请求能力
限流:应用限流(nginx,tomcat设置线程池,最大请求数),服务限流(限流算法,令牌桶/漏桶),MQ堆积消息,用户请求限制(单位时间内访问接口次数)【主要保证每台机器能够处理自己能力之内的任务】
使用多线程开发,提高机器处理能力
动静分离
1、数据拆分
动静分离的首要目的是将动态页面改造成适合缓存的静态页面;第一步就是分离出动态数据,主要从以下两方面进行:
- 用户,用户身份信息包括登录状态及登录画像等,通过动态请求获取;
- 时间,秒杀时间是由服务端统一管控的,可以通过动态请求获取;
2、静态缓存
剩下的数据是静态数据,例如商品信息,商品图片,页面js/css等等,进行合理的缓存(生成静态页面)
静态数据缓存到哪里?浏览器,CDN,服务端
用户从CDN获取到静态数据
秒杀活动
秒杀一般在12306抢票或者电商举行的一些活动时遇到。对于一些稀缺或特价商品,电商网站一般会在约定时间点,在秒杀页面对其进行限量销售。
秒杀相关特点:
- 秒杀时大量用过户会在同一时间同时进行抢购,网站瞬时访问流量激增。(高并发)
- 秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。(防止超卖现象)
- 秒杀业务流程,下订单,减库存,支付(秒杀肯定是为了商品,一般不会有订单过期,取消订单,增加库存)
从架构视角来看,秒杀系统本质是一个高性能、高一致、高可用的三高系统。
设计流程
1、限流
2、将库存放到redis中、接收用户请求的时候。从redis取库存,判断库存量是否大于本次订单购买量
库存大于本次购买量:扣减redis中的库存、并且将订单信息推送到MQ;
库存小于本次购买量:直接返回、数量不足。
3、MQ消费者获取消息:
1):更新数据库库存(乐观锁)
2):生成订单信息,扣除用户账户的订单金额(余额不足的话、将本次购买量加回到库存里)
3):异步通知用户购买结果。
原文地址
令牌机制实现秒杀业务(推荐)
利用定时任务将某些商品在规定时间之后要开启秒杀,根据库存量同步到Redis中。根据每一个商品产生对应的token数量,采用Redis中的List数据类型存储每个商品的令牌。(采用List数据类型存储的原因主要是每一个线程从List中pop时是单线程处理的,所以每一个线程拿到的令牌就不可能是同一个了)
在秒杀期间,每一个用户都去获取对应商品中的令牌(判断令牌是否有效-非空判断,如果是已经被支付过的token就不能再次被抢购)
超时未支付,归还令牌 支付成功,产生订单,标记当前令牌已经被支付,同时删除当前令牌。
原文链接
订单防止超卖
现象
- 不同用户在读请求的时候,发现商品库存足够,然后同时发起请求,进行秒杀操作,减库存,导致库存减为负数;
- 同一个用户在有库存的时候,连续发出多个请求,于是生成多个相同订单;
原因
数据库底层的写操作和读操作可以同时进行;写操作默认带有隐式锁,但是读操作默认是不带锁的;所以当用户1去修改库存的时候,用户2依然可以都到库存为1,所以出现了超卖现象。
方案分析
(1)Sql限制
在更新数据库减少库存时,进行库存限制条件(排他锁,也就是写锁起到了作用)
并采用数据库乐观锁(version版本号,CAS原理),如果限定条件不满足或版本号不一致就无法成功;
update table set count=count-1,version=versiono+1 where version=version and (count-1)>0;
(2)使用Redis对列实现
将要促销的商品以对列的方式存入Redis中;每当用户抢到一件商品则从队列中删除一个数据,以确保商品不会超卖。(将多线程变为单线程读写)
(3)使用Redis原子性操做命令(decr,incr),移除后同步更新数据库记录;
(1)数据库加唯一索引限制
对于生成多个相同订单,在UserId和OrderId上加唯一索引。
订单超时如何处理
原文地址
在开发中往往会遇到一些延时任务的需求
- 生成订单30分钟未支付,则自动取消
- 生成订单60秒后未支付,给用户发短信
延时任务和定时任务的区别在哪里呢?
- 定时任务有明确的触发时间,延时任务没有
- 定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期
- 延时任务一般是单个任务
方案分析
(1)数据库轮询
通过线程定时的去扫描数据库,通过订单时间来判断是否有超时订单,然后进行操作。
使用场景:小型项目
缺点:存在延迟(定时任务扫描的间隔),严重影响数据库性能(假如数据量过大,每几分钟扫描一次,影响其他业务)
(2)JDK延时对列
JDK自带的DelayQueue,是一个无阻塞队列,该队列只有在延迟期满的时候才能从中获取元素。放入DelayQueue中的对象,是必须要实现Delayed接口的。
方法:
- poll():获取并移除队列的超时元素,没有则返回空。
- take():获取并移除队列的超时元素,如果没有则wait当前线程,直到有元素满足超时条件,返回结果。
package com.test.thread;import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;public class OrderDelay implements Delayed {private String orderID;private long timeout;public OrderDelay(String orderID, long timeout) {this.orderID = orderID;this.timeout = timeout;}/* 返回距离你自定义的超时时间还有多少 */@Overridepublic long getDelay(TimeUnit unit) {return timeout - System.currentTimeMillis();}@Overridepublic int compareTo(Delayed o) {if (o == this) {return 0;}OrderDelay t = (OrderDelay) o;long d = getDelay(TimeUnit.SECONDS) - t.getDelay(TimeUnit.SECONDS);return d > 0 ? 1 : 0;}public void execute() {System.out.println(orderID + "\t订单要被删除啦");}public static void main(String[] args) {LinkedHashMap<String, Integer> contains = new LinkedHashMap<>();contains.put("order_0", 5);contains.put("order_1", 4);contains.put("order_2", 10);contains.put("order_3", 12);DelayQueue<OrderDelay> queue = new DelayQueue<>();System.out.println("任务添加到延时队列中");for (Map.Entry<String, Integer> entry : contains.entrySet()) {queue.put(new OrderDelay(entry.getKey(), (1000 * entry.getValue() + System.currentTimeMillis())));}long start = System.currentTimeMillis();while (true) {try {queue.take().execute();System.out.println("执行时间:\t" + (System.currentTimeMillis() - start));} catch (InterruptedException e) {e.printStackTrace();}}}
}
优点:效率高,任务触发时间延迟低
缺点:数据存储在内存中,服务器重启,数据丢失;内存限制(订单过于庞大);
(3)时间轮算法
用Netty的HashedWheelTimer实现
package com.test.thread;import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;import java.util.concurrent.TimeUnit;public class HashedWheelTimerTest {static class MyTimerTask implements TimerTask {boolean flag;public MyTimerTask(boolean flag) {this.flag = flag;}@Overridepublic void run(Timeout timeout) throws Exception {System.out.println("删除订单");this.flag = false;}}public static void main(String[] args) {MyTimerTask timerTask = new MyTimerTask(true);Timer timer = new HashedWheelTimer();timer.newTimeout(timerTask, 5, TimeUnit.SECONDS);int i = 1;while (timerTask.flag) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(i + "秒过去了");i++;}}
}
优点:效率高,代码复杂度比DelayQueue低,任务触发时间延迟比DelayQueue低
缺点:数据存储在内存中,服务器重启/宕机,数据丢失;内存限制(订单/任务数过多);
(4)Redis缓存
Redis的zset是一个有序集合,每一个元素都关联了一个score,通过score排序来获取集合中的值。
实现:将订单超时时间戳与订单号分别设置为score和member,系统扫描第一个元素判断是否超时。
(5)使用消息队列
RocketMQ延时消息(只支持特定级别的延迟消息,1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h)
RabbitMQ有延时对列
秒杀活动,提高性能,防止超卖,订单超时相关推荐
- 【Java秒杀方案】11.功能开发-【商品秒杀及优化】防止超卖 接口优化(redis预减库存,内存标记减少redis访问,RabbitMQ异步下单) 安全优化(隐藏秒杀接口,验证码,接口防刷)
商品秒杀核心功能及优化 1. 正常秒杀流程 在商品详情页面等待秒杀倒计时–http://localhost:8080/goodsDetail.htm?goodsId=2 倒计时为0,开始秒杀,点[秒杀 ...
- 秒杀 mysql 事务_秒杀怎么样才可以防止超卖?基于mysql的事务和锁实现
Reference: http://blog.ruaby.com/?p=256 并发事务处理带来的问题? 相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从 ...
- java秒杀怎么防止超卖_秒杀系统是如何防止超卖的?
秒杀系统介绍 秒杀系统相信网上已经介绍了很多了,我也不想黏贴很多定义过来了. 废话少说,秒杀系统主要应用在商品抢购的场景,比如: 电商抢购限量商品 卖周董演唱会的门票 火车票抢座 - 秒杀系统抽象来说 ...
- 淘宝如何解决超卖问题
这篇文章是我从某文库爬下来的,放在这里供大家学习. 淘宝超卖现象的产生及解决方案 一.什么是超卖现象? 超卖即"超卖缺货",当宝贝库存接近0时,如果多个买家同时付款购买此宝贝,将 ...
- 【Redis】实战篇:优惠卷秒杀 (库存超卖问题、一人一单问题)
文章目录 3.1 全局唯一ID 3.2 -Redis实现全局唯一Id 3.3 添加优惠卷 3.4 实现秒杀下单 3.5 库存超卖问题分析 3.6 乐观锁解决超卖问题 3.7 优惠券秒杀-一人一单 3. ...
- php 有关秒杀防止超卖面试题
秒杀,怎么防止库存超卖 所谓秒杀,就是网络卖家发布一些超低价格的商品,所有买家在同一时间网上抢购的一种销售方式.由于商品价格低廉,往往一上架就被抢购一空,有时只用一秒钟. 先来就库存超卖的问题作描述: ...
- 用分布式锁来防止库存超卖,但是是每秒上千订单的高并发场景,如何对分布式锁进行高并发优化来应对这个场景?
用分布式锁来防止库存超卖,但是是每秒上千订单的高并发场景,如何对分布式锁进行高并发优化来应对这个场景? 转载 codeing_doc 最后发布于2018-11-23 09:44:41 阅读数 1073 ...
- Redis高并发场景下秒杀超卖解决
目录 1 什么是秒杀 2 为什么要防止超卖 3 单体架构常规秒杀 3.1 常规减库存代码 3.2 模拟高并发 3.3 超卖现象 3.4 分析原因 4 简单实现悲观乐观锁解决单体架构超卖 4.1 悲观锁 ...
- 分布式商城系统架构中的超卖问题深度剖析(从问题原因到解决方案到优化总结)以及重复下单问题
超卖问题(包含重复下单问题) 背景 首先,超卖问题的出现是由于高并发环境下,大量秒杀请求同时发给服务端导致的秒杀商品的销售数量>其库存数量的问题.其本质就是并发场景下,多线程或者多进程对共享资源 ...
最新文章
- 新电脑一般javaweb配置
- java实现metro风格_Metro风格的Java组合框(JMetro)–重新介绍
- 百度相关搜索软件_不太热门的办公神器软件篇搜索相关
- 判断form表单里面的元素属性是否有数据_html form标签的action属性是什么意思?又有哪些用法?(附实例)...
- 多线程学习-时间改变事件
- Radon变换——MATLAB
- DIY新浪微博Android手机客户端
- linux装在机械硬盘怎么样,电脑装了固态硬盘还能再装机械硬盘吗
- 强收红包漫天要价偷转黑车……滴滴网约车被指太任性
- 51单片机的一点感想
- python复利计算_python复利代码
- Java数据可视化 (JavaFX, Apache ECharts)
- windows 环境下node开发环境搭配问题
- java开发中常见的延时消息解决方案
- jenkins内部分享ppt
- [从零学习汇编语言] - BX寄存器与loop指令
- 升华思想境界,走出博士的专家路线【转帖】
- qt小项目 代码实现简易的QQ聊天 对话框的界面实现
- jsWeb Apis 05
- ADIS16400/ADIS16405带磁力计的三轴惯性传感器(1)