1. 先把库存信息预先加载到redis;
2. lua脚本保证原子性:查redis,判断是否有库存,若有,则redis预减库存,执行3;否则直接返回;
3. 把秒杀消息发送给MQ,MQ要做的事:查mysql,若库存大于0,则减库存,成功则插入订单;否则把redis的库存加回去(消费失败,补库存)。(这条消息加上事务注解,保证ACID特征)
肯定不会超卖,但极端情况有可能发生少卖的情况:
A. redis预减库存成功,但没把秒杀消息发出去,挂了,解决方式:再次上线时,重新加载库存信息到redis;
B. redis预减库存成功,但消息发送失败,超过了重试次数,解决方式:补库存;
C. 消费者消费失败(根据具体业务来判定什么叫消费失败),解决方式:补库存。
防止同一用户重复秒杀:订单号是唯一的,肯定能防止重复(插入时发现违反唯一性,报错,回滚),但可以做个优化,在redis查库存前,用redis的setnx命令把用户id(保证唯一性即可)存入redis,设置失效时间,若发现已存在,直接返回。