仓储系统

仓储系统 #

系统划分 #

  • 库存系统:又名平台方库存,属于逻辑概念。管理商品的可销售数量,数据可能来自多个仓库之和。
  • 仓库系统:又名仓库方库存,属于物理概念。管理商品的实际存储量。商品在仓库中以库位形式保存,买家购买商品后只要没有仓库,仓库数据就不会改变。

库存系统和仓库系统需要实时同步,日切对账来确保数据一致性。如果仓库系统有物件损坏没有及时上报,而库存系统没有发现进行正常售卖,就会出现超卖问题。

库存系统 #

数据建模 #

  • 库存表:sku_id、parent_sku_id、总库存数、剩余库存数。

业务流程 #

  • 商品入库/出库:通过仓库系统的消息调整库存数量。
  • 用户下单:锁库存。
  • 库存系统和仓库系统按日对账。

下单锁库存 #

为什么下单锁库存?

  • 如果加入购物车时锁库存,可能会出现单个用户将所有商品都加入购物车导致其他人都无法购买。
  • 如果支付成功才锁库存,可能会出现大量用户下单成功却无法支付。
  • 如果下单时锁库存,可以选择一定时间未支付后自动释放库存。

回滚库存时机

  • 用户下单后超时未支付。
  • 用户支付后发起退款。
  • 风控识别到异常行为,强制取消订单。
  • 系统异常。

库存-仓库对账 #

  1. 库存系统将前一天的库存快照保存到对账系统中。
  2. 仓库系统生成库存快照文件。
  3. 对账系统拉取仓库系统的快照文件,标准化后入库。
  4. 对账系统根据双方前一天的数据进行对账,对账依据为同一订单编号的库存变动数量。
    • 库存系统通常按账户管理商品,一种商品只有一条记录;
    • 仓库系统可能是按库位管理,一种商品可能分布在多个库位上。
  5. 对于日切时发生的异常数据,可以在每天2点前生成0点到2点前的库存快照。
  6. 对账完成后,如果有一方存在数据缺失,可以以0点到2点的库存快照为基准,再次进行对账。

库存处理 #

库存扣减方案

  • 基于数据库扣减。实现简单,但存在性能限制,不适合高并发系统。
  • 基于Redis decr原子扣减,返回值小于0时扣减失败。可能出现少卖,需要重新更新库存。
    • 例如:当前库存为1,第一个请求3件商品,第二个请求1件商品,因为第1个请求导致库存变为-2,所以第2个请求也没卖完。
  • 基于Redis decr原子扣减+Lua脚本进行条件判断。
  • 基于判决值(judge)。将减法变成加法,利用整数溢出时返回错误确保不会超卖和少卖。
    1. 计算判决值(judge)保存到Redis中,judge=Long.MAX-实际库存数。
    2. 订单请求到来时,对judge执行incr(订单中货物的数量),正常返回则创建订单,此时judge值也被更新。当和超过Long.MAX时Redis会返回错误,judge值不会发生变更。
    3. 如果订单创建失败需要回滚,则对当前judge执行decr(订单中货物的数量)。

仓库系统 #

业务流程

  • 商品入库:完成采购单后,仓库系统调整商品数量,通知库存系统。商品未入库时属于在途库存。
  • 商品出库:商品发货后调整商品数量,通知库存系统。
  • 仓库调拨:发起调拨申请时锁库存,调拨出库或调拨入库后释放库存。
  • 采购商品:当库存不足时人工或系统自动创建采购单。为了满足供应商的订货要求,还需实现凑单。
  • 凑单:基于初始订单(如采购单),按照供应商预设的订货条件和当前库存最大供应时长选择凑单物品。订货条件可以是目标数量或目标金额。

自动凑单 #

订货条件为目标数量时

  1. 获取原始采购单,当采购数量不满足订货条件时发起凑单。
  2. 从仓库中优先挑选库存低于指定阀值的商品,默认凑单量为0。
  3. 计算各商品的下次补货时间。下次补货时间=(当前库存+原始采购单订货量+凑单量)/平均日出货量。
  4. 从下次补货时间最短的候选商品中选择一件作为凑单商品,以平均日出货量的一半作为该商品的凑单量。
  5. 检查当前订货条件是否满足目标数量。不满足时重新计算下次补货时间,更新凑单量,直到满足订货条件。