1,超卖问题的话,我们一般是通过事务来解决,sql语句中直接将更新和查询放在一起,通过行锁
startTransaction();
try{
int remainder = statement.query("select remainder from stock where stock_id='$STOCK_ID$'"; 得到此刻库存
然后根据订单要求数量来进行比较,如果库存大于等于订单要求数量,就执行减坤村操作
}catch(Exception e){rollback();}
commit(); 这个时候就出现超卖问题了,因为上面三个用户同时抢购的时候,进入这个事务,然后同时执行update操作,
行锁这个时候排他性,会将update操作串行化,所以最后就发生了超卖 就是上面是两步操作,所以不可以
2,简单的是将上面两句语句合并为一句 update stock set remainder=remainder-amount where stock_id='$STOCK_ID$' and $remainder>=$amount 这样就可以了
3,但是高并发下的情况又不一样了 因为太多的update 都需要等待锁,大量的请求超时
通过redis来减库存
a,监听相关物品的库存信息,事务开启前保证物品库存不被修改
b,获取现有库存信息,判断满足条件
c,如果满足就进行扣除操作
d,如果第一步有人更新了物品库存信息,进行重试
e,如果库存为0或者剩余库存不满足当前订单扣除数量就退出。
秒杀服务的实际操作版本:
用户秒杀请求到达api网关,先到redis(没有了就做一个标记)中判断秒杀的商品是否库存售罄,如果售罄直接返回秒杀失败
基于令牌桶算法的rateLimiter进行限流,得到令牌的请求才可以继续进入到下面的逻辑
要调用下游秒杀服务的接口,因为网关的并发处理能力大于下游的秒杀服务的处理能力,所以要通过排队来限制涌入下游系统的流量,
进入秒杀服务后,要考虑到分布式事务的问题,因为秒杀服务扣减库存,然后订单生成订单和邮寄的相关信息,要保证信息的一致性,采用MQ,可靠消息最终一致性方案 MQ事务消息来实现。
mq发送方在秒杀服务中,本地事务执行成功以后,也就是扣减秒杀库存成功以后,就可以返回秒杀下单成功了