1. 秒杀应该考虑的问题

1. 超卖

一般秒杀都是有库存限制的,超出秒杀库存,一个是公司实际利益受损,另外就是真的库存不足,需要练习客户退单,影响较大。

2. 高并发

秒杀都是定时或者限时秒杀,用户都在一个点进入,就算引入缓存也有可能缓存穿透或者缓存雪崩

3. 接口防刷

用户使用脚本或者机器发大量请求。

4. 秒杀url

大部分是前端直接按钮禁用,但是熟悉web开发的,会分析你的url以及参数,使用3进行大量请求

5. 数据库设计

最好单独数据库进行秒杀功能,这样不会影响主体业务

2. 秒杀系统的设计和技术方案

1.秒杀系统数据库设计

独立数据库,单独给秒杀活动使用,这样就算秒杀崩了也不会影响整个服务器。但是实际使用中这个有点麻烦,需要同步商品表,用户表,用户地址表,后面数据回归等问题,而且现在秒杀商品很多是允许加入购物车等。

2.秒杀url的设计

防止用户根据固定url进行刷单,将秒杀url动态化,整个url完全随机,通过加密url,后台解密验证url,通过才允许秒杀。这个方案挺好的。也有说法,类似于发彩票的方案,是否允许参与秒杀在发放url的时候就已经确定了,类似于彩票刮奖,实际上是假的排队。

3.秒杀页面静态化

这个方案也是需要将秒杀业务完全独立剥离出来,实际过程中,秒杀页面有可能跟普通购买页面是混合的。

4.单体redis升级为集群redis

提升缓存系统的负载能力。

5.使用nginx

6.精简sql

学到了新姿势,之前从来没考虑到这个问题。

典型的一个场景是在进行扣减库存的时候,传统的做法是先查询库存,再去update。这样的话需要两个sql,而实际上一个sql我们就可以完成的。可以用这样的做法:

update products set stock =stock-1 where goos_id =1 and version = 1 and sock>0;

这样的话,就可以保证库存不会超卖并且一次更新库存,还有注意一点这里使用了版本号的乐观锁,相比较悲观锁,它的性能较好。

7.redis扣库存

很多请求进来,都需要后台查询库存,这是一个频繁读的场景。可以使用redis来预减库存,在秒杀开始前可以在redis设值,比如redis.set(goodsId,100),这里预放的库存为100可以设值为常量),每次下单成功之后,Integer stock = (Integer)redis.get(goosId); 然后判断sock的值,如果小于常量值就减去1;不过注意当取消的时候,需要增加库存,增加库存的时候也得注意不能大于之间设定的总库存数(查询库存和扣减库存需要原子操作,此时可以借助lua脚本)下次下单再获取库存的时候,直接从redis里面查就可以了。

8.限流

秒杀最终的本质是数据库的更新,但是有很多大量无效的请求,我们最终要做的就是如何把这些无效的请求过滤掉,防止渗透到数据库。限流的话,需要入手的方面很多:

__前端限流:__点击了按钮之后,设置disabled,需要几秒再恢复,允许点击状态

__redis限流:__同一个用户xx秒内重复请求直接拒绝,通过redis设置过期时间,具体key根据业务设定,没有值允许进行请求,并且设置到redis,设置xx秒到过期时间,下次请求前先获取,有值则不请求。

令牌桶算法限流

9.异步下单

为了提升下单的效率,并且防止下单服务的失败。需要将下单这一操作进行异步处理。最常采用的办法是使用队列,队列最显著的三个优点:异步、削峰、解耦。这里可以采用rabbitmq,在后台经过了限流、库存校验之后,流入到这一步骤的就是有效请求。然后发送到队列里,队列接受消息,异步下单。下完单,入库(数据写入数据库)没有问题可以用短信通知用户秒杀成功。假如失败的话,可以采用补偿机制,重试。

比如用户到了结算页面,点击提交之后,数据直接写入消息队列,不需要等数据返回成功,可以在页面显示处理中,然后后端异步处理生成订单等步骤,这边可以用轮询(?其它方案)返回了在告诉用户成功,一定程度避免了多点查询库存,容易超卖问题。

10.服务降级

Hystrix

参考

https://www.cnblogs.com/wyq178/p/11261711.html