秒杀系统实现思路
秒杀系统,系统瞬间要处理大量并发,核心问题在于如何在大并发的情况下能保证 DB能扛得住压力,因为高并发的瓶颈就在于DB。如果说请求直接从前端透传到DB,显然,DB是无法承受几十万上百万甚至上千万的并发量的,这里就用到了另外一个非常重要的组件:消息队列。我们不是把请求直接去访问数据库,而是先把请求写到消息队列中,做一个缓存,然后再去慢慢的更新数据库。
思路
- 系统初始化,把商品库存数量加载到Redis上面来。
- 后端收到秒杀请求,Redis预减库存,如果库存已经到达临界值的时候,就不需要继续请求下去,直接返回失败,即后面的大量请求无需给系统带来压力。
- 判断这个秒杀订单形成没有,判断是否已经秒杀到了,避免一个账户秒杀多个商品,判断是否重复秒杀。
- 库存充足,且无重复秒杀,将秒杀请求封装后消息入队,同时给前端返回一个code (0),即代表返回排队中。(返回的并不是失败或者成功,此时还不能判断)前端接收到数据后,显示排队中,并根据商品id轮询请求服务器(考虑200ms轮询一次)。
- 后端RabbitMQ监听秒杀的订单信息,获取到传入的信息,执行真正的秒杀之前,要判断数据库的库存,判断是否重复秒杀,然后执行秒杀事务(秒杀事务是一个原子操作:库存减1,下订单,写入秒杀订单)。
代码实现
1.将要秒杀的商品生成对应商品数量token存储到 redis,减轻数据库压力
/**
* 采用redis数据库类型为 list类型 key为 商品库存id list 多个秒杀token
*
* @param seckillId 商品id
* @param tokenQuantity 令牌数量,对应商品数量
* @return
*/
// 采用redis数据库类型为 list类型 key为 商品库存id list 多个秒杀token
@RequestMapping("/addSpikeToken")
public BaseResponse<JSONObject> addSpikeToken(Long seckillId, Long tokenQuantity) {
// 1.验证参数
if (seckillId == null) {