之前完成了一个秒杀下单系统的开发,现在总结一下,
起因
业务场景是社区电商, 疫情期间,社区准备了一批口罩,打算分批让用户抢购。那时我们还在过年,客户也没通知我们,然后我们这个小电商系统,在当晚就挂掉了。下面是我们当时的架构:类似下面
稍微思考到这个架构的问题,用户下单时,redisson进行加锁时,会让其他线程等待。所以在并发大时,会导致tomcat连接数超限。后面查询日志也证明了如此,所以当时临时的解决方案是,重启+增加应用容器。总算勉强度过。
后面我们紧急改造了我们的业务j流程,变成了下面这样:
主要做了两点改变:
1.将查询库存放到了redis中
2.将实际的下单功能放到了,MQ应用中。
这样极大的提高了前端应用的承载能力,经实战测试 2万的口罩,3分钟内全部卖空,系统平稳运行。
首先先看一下我们现在具体的秒杀流程。
秒杀流程概览:
文字表述如下:
- 客户提交订单,包含多个商品
- 后台应用
- 对商品id加锁,
- 循环判断商品库存是否在redis中存在
- redis没有库存,从数据库查询库存,减去本次构购买数,放入到redis中
(如果库存不够此次贩卖,将数据库库存放入到redis中) - redis中有库存,减去本次购买数,重新放到redis中。
- 解锁生成订单号,将本次订单信息发送到mq,异步消费
- 返回订单号,
- 客户端获取订单号,轮询查询订单状态。
- MQ消费应用
- 对商品,促销商品,优惠券,积分进行加锁
- 扣减优惠券,积分,生成订单,扣减库存,保存订单信息
- 将订单结果存入redis。解锁
- 完成实际下单
- 前端轮询查询订单是否下单成功
问题
看完整个详细流程,已经解决了主要的高并发问题,现在讲讲实际遇到的一些问题:
1.如果redis缓存被清空了怎么办?
因为我们公司目前将redis的作用就是缓存,所以当redis快满了时,运维会将redis进行清空。(虽然有做redis 内存淘汰侧率,但是主要是总有人使用redis时没设置redis的过期时间),所以redis缓存失效的情况,需要考虑,所以我们mq消费时,会重新加锁,如果发现库存不足,会将结果存入redis。前端通过oderNo轮询就知道订单失败。
2.库存不足
情景一 :提交两个商品,一个redis 库存够,另一个redis库存不够如何处理?
情景二:提交两个商品,一个在redis中有库存,但库存不足,另一个redis没有库存,需要从数据库查询,但库存足够本次购买:
答:这个我们处理思路是,判断是否超卖 循环中判断,但是所有redis存库存的操作,解锁前统一保存到redis。(我们是 redis cluster,分片了不支持事务);
3.秒杀开始后能否修改库存?
这个当时我们是考虑到库存存到redis了,并设置过期时间,所以无论商家改大或者该小,都不会导致错误。但是,由于MQ消费消息,是有并发锁的,这个时候去看的库存,是不真实的,因为有许多订单是已经提交,但是没有落库。所以我们禁止了秒杀开始后修改库存