秒杀系统的核心点都在这里,快来取

时间:2024-03-21 22:14:27

总括:

1,分布式限流

2,使用缓存,避免大量查询直接怼DB

3,乐观锁,CAS,并发处理

4,异步处理,避免大量数据怼到DB

目录

  1. 前言
  2. 业务流程
  3. 查看商品详情
  4. 校验库存
  5. 扣库存
  6. 何时扣库存
  7. 分布式限流
  8. 异步化
  9. 总结

前言

今天老顾尝试一下用一篇文章来介绍一下秒杀系统,对老顾来说是个挑战,因为秒杀系统涉及的技术点是很多的,不可能用一篇文章就让小伙伴们可以完整的搭建秒杀系统,但老顾会把秒杀的核心设计,用到的技术尽量说全点,说的通俗点。

悄悄的告诉你,之前老顾分享文章中的知识点,在秒杀系统就用上了,老顾会直接引用了。

业务流程

秒杀系统的核心点都在这里,快来取

 

上面图中,描述了常规的秒杀业务,具备几个业务特性

1、倒计时开场,到点开通购买

2、价格便宜,库存少

3、一般业务用户限制,一个用户只能购买一个商品

业务流程其实个普通的购物流程,没有什么区别

1、查询商品详情

2、点击购买,校验库存

3、扣库存,创建订单

4、去支付

秒杀系统的核心点都在这里,快来取

 

我们梳理了秒杀的流程,那我们按照流程一一分析,各个环节怎么去设计

查看商品详情

秒杀商品详情里面有个特殊的业务,就是秒杀开始的时候才能对这个商品进行购买,在没有开始之前是不能购买的(购买按钮隐藏或置灰)

小伙伴们回想一下,自己参与抢购秒杀商品的时候,是不是在没有开始秒杀之前,会一直狂刷商品页面,尤其是临近开场的时候,查看商品的请求会猛增。怎么让商品详情业务能够hold住这么大量的请求呢?你了解大型网站的页面静态化吗?

这个商品详情的方案,老顾以前的文章介绍过,非常详细,大家可以去查阅。

这里额外要说的就是开场倒计时。这个倒计时的效果由客户端实现,不过时间一定是同步的服务器端,客户端可以每隔几分钟同步一次,再重新倒计时

还有就是秒杀时间是否已到,一定是由服务器端进行判断的,而不是由客户端时间决定。

校验库存

这个就是检查商品库存有没有货,没有就直接返回,有就进入购买流程。我们发现检查库存走的是数据库,如下:

秒杀系统的核心点都在这里,快来取

 

这个小伙伴们是不是发现有问题了,这么大量的请求过来,走数据库肯定是不行的,想到的肯定是把库存放到缓存中,因为是分布式,所以放到Redis中

那就有个问题了,什么时候把商品库存放到缓存中?

最好的方式就是通过后台管理系统,因为我们新建一个秒杀商品,肯定是通过后台管理系统操作的,添加秒杀商品后,应该会有一个类似按钮【上线发布】,可以在这个按钮事件进行缓存,当然小伙伴们可根据自己的业务。

扣库存

一、秒杀扣库存,经常会出现【超卖】,这个是什么原因呢?我们看一下扣库存的代码:

秒杀系统的核心点都在这里,快来取

 

直接执行sql语句,把库存减一。我们再看一下整体代码,先检查库存,有库存再扣库存

//验证是否有库存
if(checkGoodStock(goodId) <= 0){
 return 0; //表示无库存了
}
//扣库存
deductGoodStock(goodId);

二、举个例子:

如:我们现在的剩余库存为1,同时有2个线程请求过来,又同时执行到验证库存代码块,这样就都通过了验证,然后又先后执行了扣库存,但扣库存是直接减1的,导致库存变成了-1,超卖就此形成了

核心导致超卖就是因为大量请求到来,引起并发问题;多个线程是被CPU调度执行的,各线程间交替执行业务如何利用锁,防止缓存击穿?重构思想的重要性

怎么去解决这个问题?老顾之前介绍过分布式排他锁和乐观锁,都可以解决,可自行查阅。

三、这里因为业务表的设计,我们用另一种方式,稍微修改一下sql语句

DBUtils.ExecuteSql(" update goods set stock = stock - 1 where goodId = ? and stock > 0" , goodId);

在where语句加了stock > 0 ,因为update执行的时候产生行锁,其他线程只有等当前线程执行完后才能去继续执行这个update语句。又因为加了stock > 0就保证了不可能出现负数出现超卖

秒杀系统的核心点都在这里,快来取

 

何时扣库存

一、扣库存业务有两个选择

1、方案一:下单减库存,即当买家下单后,商品库存减去买家购买的数量

2、方案二:付款时库存,即当买家下单后,并不立即减库存,而是等用户付款后才减库存

这两个方案都有各自的问题,要从业务上面来分析。

  1. 方案一的问题,下单就把库存减了,如有竞争对手通过恶意的方式将商品全部下单,让商品库存为0了,但最后他是不会付款的。这样导致正常的用户购买不了,卖家也没有得到钱。
  2. 方案二有效解决了上面的问题,但是他出现的问题是,因为下单是不减库存的,只有付款后才减,那如果商品数量是10个,那很有可能有100个用户下了订单,都去付款支付,最后只有10个人能付款成功,90个人付款不了,这样就让用户体验很差,明明已经可以下单,但最后还是没有买到商品。

二、那怎么去选择?

一般我们在秒杀场景下我们选择方案一,小伙伴们有没有注意其他的平台,在下完单后,会有一定的时间去支付,一般设置半个小时。

其实本质就是让用户有半个小时支付时间,如超过不支付,就失效此订单,并且回收库存

所以我们买家经常会去捡漏,这是很多系统常用的方式。

三、用户标记

即使有了上面的方案,其实还是会存在恶意的问题,只是我们把库存回收了。但还是有缓存的时间延迟半小时。

可在这基础上继续优化,加入风控系统,一般大中型平台都会有风控系统。这里老顾就讲最简单的方式。也就是我们要监控哪些用户一直是下单不付款的,如果超过一定的阀值,就给这个用户打上标记,归属黑名单。

还有就是在商品限制购买数量,经常使用的是每个用户限制购买1~2件,也能规避恶意下单

分布式限流

秒杀场景一般都是商品稀缺,提供出来的数量比较少,如就100个,但很便宜,那就导致同一时间很多用户都会来抢,如100万用户来抢100个资源,其实有90%的用户请求是无效的,因为只有100个商品,也只有100个人能够买到。那100万个用户请求过来对系统来说是没有必要的,反而增加了系统压力。其实我们可以只允许5000个用户(业务评估出来的数值)进入业务系统,其他请求的直接返回就行了。

小伙伴们会不会说那不是不公平吗?其实大家仔细想想,秒杀拼的就是网络快,手快,和人品;没有什么公平不公平的

怎么解决?就是利用分布式限流方案,有很多中方案,Nginx限流,业务系统有计数器法,漏斗法,令牌法,网上有很多介绍;一般基于redis进行令牌限流,有很多组件,一般采用google提供的。老顾这里就不多作介绍,以后可以和大家专门分享限流知识点。

异步化

一、思路由来

我们大家想一想,即使我们做了上面的限流机制,卡住了绝大部分请求到业务系统中,但还是有很多请求进入了业务系统。如果我们商品库存数为5000个库存,在大流量的时候,会有很多用户都有资格去下单,那也就是同时会有5000个并发去操作mysql数据库。那数据库也是抗不住的,一般mysql的并发量500左右。

这个时候我们需要做一些异步处理,让5000个下单请求进入消息队列,让订单消费服务去慢慢处理5000个请求,这样就有效的把并发同步请求,改为了串行异步

二、前端改造

当然改成了异步处理,前端在下单的时候,就没法立刻得到下单的结果,所以前端要做一个【抢购中。。。】的状态页面,在此页面中定时轮询下单结果。这样就大大提升了系统的吞吐量,降低了系统压力。

消息队列中间件有很多选择,一般选择RabbitMq,RocketMq

总结

今天老顾把秒杀系统的核心知识点做了介绍,最主要的就是库存的处理思路,以及限流和异步化,商品详情其实是很重要的,大家要去看一下【你了解大型网站的页面静态化吗?

还有一些风控方面的知识没有介绍全,如:如何隐藏下单接口URL,规避恶意攻击

其实这种大流量的系统,解决的核心思想就是

1、尽量把请求拦截在最外层

2、保护数据库资源,尽量采用缓存

3、同步操作异步化

4、尽早失败,保护业务系统

谢谢大家!!!


-End-

如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!

10几年的经验实战分享

相关微服务,分布式,高并发,高可用,企业实战,干货等原创文章正在路上

欢迎关注头条号:老顾聊技术

精品原创技术分享,知识的组装工

推荐阅读

1、你了解如何利用token方式实现分布式Session吗?

2、Mysql索引结构演变,为什么最终会是那个结构呢?让你一看就懂

3、一场比赛涉及到的知识,用通俗易通的方式介绍并发协调

4、企业实战Redis全方面思考,你思考了吗?

5、面试题:Thread的start和run的区别

6、面试题:什么是CAS?CAS的作用以及缺点

7、如何访问redis中的海量数据?避免事故产生

8、如何解决Redis热点问题?以及如何发现热点?

9、如何设计API接口,实现统一格式返回?

10、你真的知道在生产环境下如何部署tomcat吗?

11、分享一线互联网大厂分布式唯一ID设计 之 snowflake方案

12、分享大厂分布式唯一ID设计方案,快来围观

13、你想了解一线大厂的分布式唯一ID生成方案吗?

14、你知道如何处理大数据量吗?(数据拆分篇)

15、如何永不迁移数据和避免热点? 根据服务器指标分配数据量(揭秘篇)

16、你知道怎么分库分表吗?如何做到永不迁移数据和避免热点吗?

17、你了解大型网站的页面静态化吗?

18、你知道如何更新缓存吗?如何保证缓存和数据库双写一致性?

19、你知道怎么解决DB读写分离,导致数据不一致问题吗?

20、DB读写分离情况下,如何解决缓存和数据库不一致性问题?

21、你真的知道怎么使用缓存吗?

22、如何利用锁,防止缓存击穿?重构思想的重要性

23、海量订单产生的业务高峰期,如何避免消息的重复消费?

24、你知道如何保障生产端100%消息投递成功吗?

25、微服务下的分布式session该如何管理?