扣库存流程

1,同步扣库存

在订单生成的时候就扣去库存,存在一些问题:

  • 会造成订单被取消,实际库存扣了,但是没有支付,也就造成了少买的情况
  • 即时扣库存,并发性太差

2,异步扣库存

对于电商网站,比如某东,会在订单支付成功后,有一个出库的过程,有可能出库成功,也有可能出库失败。

库存有两部分:一个缓存Redis层, 一个数据库MySQL层

2.1当客服新增了5个库存,那么,缓存Redis和数据库MySQL层都需要增加5个库存,这个使用分布式事务的最终一致性来满足要么全家,要么全部不加。

2.2在订单生成的时候,需要先扣库存,先扣除Redis,生成支付订单,这时不扣除MySQL的库存

2.3当Redis库存扣完了,就无法下单了,下单失败,在外层把请求挡住

2.4用户支付订单,订单支付成功后,返回我的订单,有一个出库过程

2.5出库是一个MQ异步解耦的消息队列,这个过程是扣除MySQL库存,如果扣除成功,那么出库成功,完成订单的整个流程,进入发货状态;如果扣除库存失败,则进行一些列操作1)订单状态改成取消2)返还Redis库存3)退款

3,Redis库存和MySQL库存

支付前是预扣,扣除Redis库存

支付后是真正扣除,扣MySQL库存

在一些极端情况下数据不一致

3.1如果Redis库存和MySQL库存一直,没问题

3.2如果Redis库存比MySQL库存多,超卖的订单出库失败

3.3如果Redis库存比MySQL库存少,少买,有货没卖出去

这样总体不会出问题,MySQL数据库层,保证库存不会再真正有问题

4,如何检测Redis和MySQL库存不一致

4.1暴力方法,很容易想到,找一个低峰期,比如凌晨2点,周期性强行覆盖,但是也可能在这个时候出现一个订单,出库的过程中,扣除了MySQL库存,但是没有扣除Redis库存(我是这样理解的:这时有一个订单,通过Redis的库存扣除后,然后支付进入出库状态,这时已经取出MySQL的库存数要同步到Redis中,紧接着MySQL库存扣除了,Redis的数据还是扣除前的数据,所以就会数据不一致)

这个就是数据库同步缓存的更新机制方面的问题

属于一致性的逻辑设计的问题

缓存数=数据库库存数-待扣数

当然这里面也还有其它的方案,以及考虑到一致性的要求高低,可以使用简单或复杂的方案 就看系统复杂度了,越是大系统就要拆得越细

比如待扣数又可以放到一个队列里面,或者缓存里面,同时有计数,直接读计数就行

比如放到mongo,已支付待出库的数量,一般也不会很大,count一下,也不会损失多少

所以一般系统都不能完全保障数据链不出错,但一定要有补偿,就是出错了可以纠错 要保障不出错的代价显然太大

同步是有一套刷新机制,可以定时,也可以通过MQ,或者监控不一至同步等等。。。 也叫做保障缓存数据的新鲜度

一般不会太长时间,半小时,几分钟都有可能,不同场景需求不一样

 

5.买火车票的12306,晚上的时间都不能买票,这个时间估计是在同步库存,将数据库库存同步到redis库存中, 但是买火车票之类,在订单生成前,必须扣除实际库存,也就是要扣除mysql的库存,

因为买火车票和购物不一样,购物可以付款后出库,但是买票这种,支付前就必须出库,因此,要将出库过程提前, 只有出库成功,才能生成订单,同样要引入redis库存

5.1先扣缓存中的库存,扣除成功后,然后才可以去扣mysql中的库存

5.2如果扣除缓存中的库存失败,就会挡在外面,返回库存不足,这些请求不会穿刺到mysql中,挡住了大多数的请求压力。

5.3redis库存会和mysql库存不一致,极端情况下是肯定有的,需要进行库存同步

5.3.1当缓存库存比数据库库存多,那么就会出现,查询有票,但是就无法下单,下单的时候就说库存不足, 这样也不会超卖,当redis的库存多的那部分扣完了,就可以把请求全部当在外面了。 对于12306,有时候,查询的时候有票,但是下单的时候返回库存不足,然后重新查询发现还是有库存, 这种情况应该就是redis中库存和mysql中库存不一致造成的。

5.3.2当缓存库存比数据库缓存少,那么不会出问题,只会出现有票,但是没有出售的情况,等完成库存同步一下, 明天又准确了。

5.3.3当然,mysql扣除库存的部分,还需要在前面加入队列缓冲,避免请求过多,让应用程序或数据库崩溃。

本文参考:https://my.oschina.net/134596/blog/3129705

猜你喜欢

转载自www.cnblogs.com/codingLiu/p/12746059.html