回顾一下下单流程:
用户发起请求
会先请求Nginx,Nginx反向代理到Tomcat,而Tomcat中的程序,会进行串行工作,
分为以下几个操作:
1 查询优惠券
2 判断秒杀库存是否足够
3 查询订单
4 校验是否是一人一单
5 扣减库存
6 创建订单
像什么一人一单,扣减库存这些地方都是要操作数据库的,所以时间会消耗比较多,效率比较低,就是说 ,他是一个串行执行的,所以就需要异步执行操作
优化方案:
我们将耗时较短的逻辑判断放到Redis中,例如:库存是否充足,是否一人一单这样的操作,只要满足这两条操作,那我们一定是可以下单成功的
其次不用等数据真的写进数据库,直接告诉用户我们下单成功就好了,后续的操作呢,后台直接再开一个线程,后台线程再去慢慢执行队列里的消息,这样就可以很快的完成下单任务了,就是先不管有没有真正的下单成功,只要点了下单优惠券就告诉用户下单成功就好了,后续的操作有点慢,得后台的线程再去慢慢的执行队列里的消息
但是存在一个问题就是:有可能已经告诉用户下单成功了,但后边出现了的问题导致其实并没有真正下单成功,那这张优惠券就用不了
这其中存在两个难点:
1 怎么在Redis中快速校验是否一人一单,还有库存判断
2 校验一人一单和将下单数据写入数据库,这是两个线程,我们怎么知道下单是否完成
整体思路:
当用户下单之后,判断库存是否充足,只需要取Redis中根据key找对应的value是否大于0即可,如果不充足,则直接结束。如果充足,则在Redis中判断用户是否可以下单,怎么判断呢?
用set集合,用户一旦下单就将用户id(userId)存入到集合当中,诶当下一次用户下单的时候,就判断一下userId是否在集合里面,如果set集合里面没有该用户的下单数据,就可以下单,如果里面有用户的userId,那就不能下单了,并将userId和优惠券存入到Redis当中,并且返回0,整个过程保证原子性的(也就是没有其他线程插入),所以需要用Lua来操作
同时由于我们需要在Redis中查询优惠券信息,所以在我们新增秒杀优惠券的同时,需要将优惠券信息保存到Redis中
看下图Lua脚本: