[分享]浅谈电商库存模型
本文讨论内容不包含秒杀,仅仅是最普遍的库存模型而已,重点讨论如何避免超卖
,少卖
。
1、 基本概念
1.1 超卖
实际库存已经为0,但是依旧卖出去了。可能导致商家无货可发。
1.2 少卖
明明有货,但是库存被锁定,导致商家货卖不出去。
2、 分布式事务简述
2.1 消息事务+最终一致性
所谓的消息事务就是基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,
它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,
要么两者都失败,开源的RocketMQ就支持这一特性,具体原理如下:
- 1、A系统向消息中间件发送一条预备消息
- 2、消息中间件保存预备消息并返回成功
- 3、A执行本地事务
- 4、A发送提交消息给消息中间件
通过以上4步完成了一个消息事务。对于以上的4个步骤,每个步骤都可能产生错误,下面一一分析:
- 步骤一出错,则整个事务失败,不会执行A的本地操作
- 步骤二出错,则整个事务失败,不会执行A的本地操作
- 步骤三出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息
- 步骤四出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务
2.2 TCC编程模式
所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。
以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。
总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。
3、 库存模型
- 1.下单的时候每个订单都会有预占库存。
- 2.订单出库或取消解锁预占。
- 3.可售库存 = 现货库存 – 预占库存
具体下单操作流程是
下单 –> 预占(预占库存+1) –> 支付 –> 出库(释放预占并减库存,涉及到分布式事务) –> 完成
重点讨论此方案的意义,跟出库部分。
3.1 为什么设计成预占库存?
以下均为个人理解
- 1.库存和下单分离,下单了不会实际减少库存。真正的实物库存跟逻辑库存分离,方便(盘库、补货等)
仅仅用实物库存
的场景跟高并发
的场景(下单)分离。 - 2.预占可以减少库存表的操作频率,减库存可以在发货的时候统一处理。(操作多次预占表后仅在必要时操作一次库存表,跟1有共通之处)
3.2 预占库存怎么加?
- 1.可以用mysql乐观锁(version递增避免aba)
- 2.可以用redis watch(同样基于cas,无aba)
3.3 出库部分怎么保证事务?
-
1.先出库还是先释放预占?
为了避免超卖选择先减库存,再释放预占。
-
2.如何保证事务一致性?
采用消息机制的2pc保证分布式事务。