如何设计一个可靠的秒杀系统

        秒杀一般出现在商城的促销活动中,指定了一定数量的商品,以极低的价格,让大量用户参与活动,但只有极少数用户能够购买成功。这类活动商家绝大部分是不赚钱的,说白了是找个噱头宣传自己。

虽说秒杀只是一个促销活动,但对技术要求不低。我们在设计秒杀系统时可以做以下优化

一 .页面静态化

        一般在进入抢购页面时,需要向服务器端发送请求,获取商品名称、商品描述等信息,如果此时有很多用户进入抢购页面,服务器端是承受不住这么大的压力的。我们可以将抢购页面作为静态页面,只有当用户点击抢购按钮时才发请求到服务器。

二 .秒杀按钮特殊化

        一般用户为了抢到商品,会在未到抢购时间时疯狂点击抢购按钮,白白给服务器端带来压力,在前端中可以考虑把抢购按钮变为灰色,只有到了抢购时间才能点击,或者限制用户在10s内只能点击几次。

三 .读多写少

        因为用户在进行商品秒杀前都需要检查库存是否足够(读),只有在库存充足的情况下才会减库存(写),由于秒杀商品有限,故大部分的情况是库存不足,这是典型的读多写少的场景。其中一种解决方案是引入缓存,缓存中和数据库中同时保存库存的数量,当用户下订单的时候,先读取缓存中的数据,如果还有库存,先扣减缓存中的库存,然后扣减数据库中的库存,没库存的时候就不会到达数据库。此外,缓存也要使用高可用的主从复制方式。

四 .超卖问题

        显然获取库存数量和更新库存不是原子操作,假设仅有1个库存时,两个线程同时获取到库存数量,并且同时进入stock>0的逻辑,那么就会扣减两次库存,导致超卖。我们可以通过加锁保证两个操作的原子性,但是效率过低。我们可以使用redis并配合lua脚本,保证判断库存数目和更新库存的原子性。

int stock = mapper.getStockById(123);//获取库存数量
if(stock > 0) {
  int count = mapper.updateStock(123);//更新库存
  if(count > 0) {
    addOrder(123); //下订单
  }
}

 五 .异步处理

        一般的秒杀系统中,秒杀成功之后,需要下单和支付,点击下单之后用户应该尽快得到下单成功的提示,并执行后续支付流程,而不用等待后续与订单数据库操作完成之后,有必要将下单服务拆分出来并用mq做异步处理。 值得注意的是在使用mq时要注意消息丢失和重复消费问题。

六 .限流处理

        秒杀场景如果不做特殊的处理,一般会有一些高手通过使用机器或程序直接请求秒杀接口,这比人们手动点击速度要快太多,显然时不公平的。我们需要对同一用户或者ip的请求次数进行限流,或者是在抢购前添加验证码验证是人为操作。

七 .重复购买

        为了防止一个用户重复秒杀多次,客户端可以生成一个能标识唯一用户的字符串,在秒杀前随着用户信息一起发送给后端,例如Token,在秒杀之前,判断该Token是否已经秒杀过了即可。


版权声明:本文为qq_55768596原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。