商城抢购/秒杀模块设计

抢购是指在某一个时间段内商家低价甚至免费销售商品,消费者突发性的集中投入购买力,大批量的、超常规的购买某种或某类商品的现象。

总结来说有以下俩个特点:

  • 商品数量有限,比如设置此次抢购一共1000个商品,抢完即结束
  • 时间受限,比如会定义从何时开始,到何时结束

14年的8月份,墨迹商城做了几次抢一毛钱抢毛巾、后续的每周蓝色星期二是抢空气果的抢购活动,当时非常火爆。墨迹本身的用户量就很大(目前有6亿用户),而且之前做了很久的宣传。大量的用户(想占便宜抢一毛钱毛巾的用户和对我们当时的新产品空气果发烧的用户)一拥而入。1毛钱抢毛巾的活动,一瞬间 1 万条毛巾就被抢光了。可以想象当时的疯狂程度……

简单抢购需求描述/抢购流程描述

1.商品详情页显示商品信息。没有开始的抢购之前,抢购按钮为灰色且不可点击。按钮显示提示文字“马上开抢”,并显示抢购开始倒计时间;
2.抢购倒计时结束,抢购按钮方可点击,并显示“抢购”;
3.用户抢购商品成功,跳转到确认订单页面,完成下单、支付流程;
4.抢购失败,跳转到抢购失败页面;
5.已抢光的商品显示“已抢光”,抢购时间结束的商品显示“已结束”;
6.抢购流程结束。

根据需求我们需要解决以下几个问题:

1、抢购之前用户会不停的刷新商品详情页面,怎么办?

答:商品详情页面静态化,包括页面中引用的静态文件、图片等都缓存到CDN节点上。至于页面中显示的倒计时,由于要抢购时间同步,所以不能使用客户端的时间。服务器完成一个简单时间接口即可,返回Json格式时间。单机 1w以上 QPS 应该没什么问题,因为本身不涉及复杂业务逻辑。在自己的 Mac Pro上测试,用 Openresty 实现的获取系统时间,能做到 7157 的QPS,服务器性一定会能更高。

2、抢购开始倒计时结束,大量用户同时抢购商品,如何应对流量暴增,是平实的几倍、几十倍?

答:肯定不能让大量的并发请求(并且是写请求)直接落到 Mysql,我们考虑用队列来实现。优先写入抢购队列,然后慢慢的写入Mysql,这样一台数据库服务器也就就够了。

抢购/秒杀,手快就能抢到,手慢就抢不到。所以为了减轻服务器的压力,也给用户好的体验。对于如果队列中抢购请求过多,远远超过抢购商品数的情况下,将直接返回“抢购失败”给用户。比如这次一共抢购100件商品,此时队列中已经有了10w个抢购请求。为了用户体验,再进来的抢购请求直接返回抢购失败。不让用户等待太久的时间(即使进入队列,很长时间才执行到,也早早的就已经没有可抢的商品了)。

3、如何限制抢购商品数量(库存)?

答:需要记录一个商品本次抢购的个数 N。每当从队列透过一个抢购请求到 DB,则 N - 1,直到 N = 0 商品被抢光。且为了保持系统简单。有用户抢购成功,但是不付款的情况,也不再考虑从新“回仓”的问题。如果考虑回仓的话,比如半个小时以后用户仍没有付款,则视为用户放弃购买这次抢购的商品。商品库存 +1,这时再去抢购仍然能抢到。就像12306抢车票设置的是45分钟后不付款,回仓,如果是老司机这个时候回去看看兴许真的还能买到票。

详细设计

略……