乐趣区

关于系统架构:vivo全球商城库存系统架构设计与实践

作者:vivo 官网商城开发团队 – Xu Yi、Yan Chao

本文是 vivo 商城系列文章,次要介绍 vivo 商城库存零碎倒退历程、架构设计思路以及应答业务场景的实际。

一、业务背景

库存零碎是电商商品治理的外围零碎,本文次要介绍 vivo 商城库存核心倒退历程、架构设计思路及应答各种业务场景的实际。

vivo 商城原库存零碎耦合在商品零碎,思考到相干业务逻辑复杂度越来越高,库存做了服务拆分,在可售库存治理的根底上新增了实物库存治理、秒杀库存、物流时效、发货限度、分仓治理等性能,满足了商城库存相干业务需要。

本文将介绍 vivo 商城库存零碎架构设计教训以及一些问题的解决方案。

二、零碎架构设计

2.1 vivo 大电商库存架构

依据 vivo 大电商的销售渠道与业务场景能够将库存业务架构分为 3 个层级:仓库层、调度层以及销售层。

仓库层对应实体仓库,包含自营仓库、顺丰仓等第三方仓库以及 WMS 零碎、ERP 零碎等;调度层负责库存调度与订单发货治理;销售层蕴含多个服务终端,vivo 官网商城、vivo 门店、第三方电商分销渠道等。其分层构造如图所示:

本文探讨的 vivo 官网商城库存架构设计,从整个 vivo 大电商库存架构来看,vivo 官网商城库存零碎波及销售层外部架构以及销售层与调度层的交互。

2.2 商城库存零碎架构演变

晚期商城的库存冗余在各业务零碎中,如可售库存在商品零碎、流动库存在营销零碎等,库存流转也只有扣减与开释,无奈针对库存进行整合与业务翻新,存在诸多限度:

  • 不能进行精细化治理,库存未分层,无奈针对实物库存、分仓策略、流动库存进行精细化治理。
  • 没有分仓策略,无奈提前获取商品收发地址,物流时效无奈估算。
  • 无奈针对地区、商品等进行发货管控。
  • 实时性差,无奈及时同步实物库存以及分仓策略。
  • 性能弱,与其余零碎耦合大,不能灵便扩大。

基于上述限度与产品冀望,21 年库存零碎实现初版架构设计,尔后零碎一直迭代欠缺,造成以后的零碎架构:

库存零碎提供两个外围能力:交易能力和库存治理。下层业务方能够调用提供的 API 实现库存查问、库存扣减等操作;治理台能够按成分仓策略、库存同步等操作。

三、零碎业务架构

3.1 库存类型 & 分仓治理

3.1.1 库存类型构造

库存零碎一共蕴含 4 类库存:可售库存、实物库存、预占库存、流动库存。

  • 可售库存:经营配置的普通商品库存,商品维度到 SKU。
  • 实物库存:由仓储零碎同步到库存零碎的实物库存,细化到具体仓库。
  • 预占库存:用户下单实现库存预占,仓储零碎发货后开释预占库存,预占库存能够监控已下单未发货库存量。
  • 流动库存:用于秒杀、抢购等各类营销流动的商品库存。

基于不同类型库存,能够构建一个简略的库存分层体系:

3.1.2 分仓治理

库存核心还保护了仓库信息、分仓策略、仓库实物库存信息等等:

  • 仓库信息:仓库根底信息,包含仓库地址、类型、编码等。
  • 分仓策略:仓库性能信息,仓库可发货区域、无实物库存后的备选仓库;订单依据收货地址对应优先发货的仓库,争取尽快发货尽早到货。
  • 仓库库存:仓库实物库存,由仓库调度零碎同步到商城库存零碎。

3.2 商城库存流转计划

商品库存流转波及两个次要操作:正向库存扣减、逆向库存回退,整套库存变更流程如下:

3.2.1 正向库存扣减流程

对于库存扣减,目前常见有两种库存扣减计划:

(1)下单时扣库存。

  • 长处是:实时扣库存,防止付款时因库存有余而阻断影响用户体验。
  • 毛病是:库存无限的状况下,歹意下单占库存影响其余失常用户下单。比如说有 100 台手机,如果没有限度下单数量,这 100 个库存可能被一个用户歹意占用,导致其余用户无奈购买。

(2)领取时扣库存。

  • 长处是:不受歹意下单影响。
  • 毛病是:当领取订单数大于理论库存,会阻断局部用户领取,影响购物体验。比如说只有 100 台手机,但可能下了 1000 个订单,但有 900 个订单在领取时无奈购买。

从用户体验思考,咱们采纳的是下单时扣库存 + 回退这种计划。

下单时扣减库存,但只保留一段时间(比方 15 分钟),保留时间段内未领取则开释库存,防止长时间占用库存。

3.2.2 逆向库存回退流程

库存回退基于库存变更日志一一回退。

库存回退根本流程:订单出库前用户申请退款,回退可售库存、回退预占库存、软删除扣减日志、减少回退日志;一旦商品出库,用户申请退货走处理机流程,可售库存和实物库存均不回退。

3.3 精细化发货管控

库存零碎还提供了一系列定制辅助性能:分仓策略、发货限度、物流时效等等。

(1)分仓策略

为了给用户更快的发货,咱们采纳的是分仓策略,即由最近的仓库(存在优先级)给用户发货;同时存在备选仓库,当所有仓库无实物库存时可走备选仓库。

3.3.1 发货限度

发货限度分地区限度工夫限度。

  • 地区限度:依据收货地址批量设置局部区域无奈发货等规定,粒度到省市区维度。
  • 工夫限度:仓库的发货时效治理,包含每天的发货时段、大促发货时段、以及非凡状况下的停发时段。

3.3.2 物流时效预估

依据用户收货地址,基于分仓策略确定发货地址,再基于发货时效确定发货工夫,晋升用户体验。

四、零碎架构技术要点

4.1 库存扣减防重

订单反复提交会导致库存反复扣减,比方用户误提交、零碎超时重试等,针对此类问题有如下常见解决方案:

  1. 订单提交按钮单击置灰,防止反复提交。注:对于按钮置灰这种计划,能够缩小用户误触反复提交的可能性,但不能从根本上解决库存被反复扣减的问题,比方通过脚本来刷扣减库存的接口,仍旧造成库存的反复扣减。
  2. 保障库存扣减接口的幂等性。注:保障接口幂等的计划有很多,比方每次扣减库存时,带上惟一的流水号,利用数据库的惟一索引保障幂等等。
  3. 采纳令牌机制。用户提交订单会进行令牌校验,校验通过能力提交订单。注:这种计划保障每次提交的订单是惟一的,如果用户屡次下单,那么会产生多个订单。

本零碎采纳的是保障接口幂等性的计划。

在库存扣减接口入参中减少订单序列号作为惟一标识,库存扣减时减少一条扣减日志。当接口反复申请时,会优先校验是否曾经存在扣减记录,如果已存在则间接返回,防止反复扣减问题,具体流程如下:

4.2 防超卖与高并发扣减计划

4.2.1 惯例渠道防超卖计划

惯例下单渠道流量小且对超卖危险讨厌度极高,罕用的防超卖计划有:

计划一:

间接数据库扣减。通过 sql 判断残余库存是否大于等于待扣库存,满足则扣减库存。该计划利用乐观锁原理即 update 的排他性确保事务性,防止超卖。

伪代码 sql:

sql:update store set store = store - #{deductStore} where (store-#{deductStore}) >= 0

该计划的 长处 是:

  • 实库实扣,不会呈现超卖;
  • 数据库乐观锁保障并发扣减一致性;
  • 数据库事务保障批量扣减失常回滚。

该计划的 毛病 是:

  • 行级锁的起因存在性能瓶颈,高并发会呈现申请梗塞超时问题;
  • 直连数据库,每次扣库存都是写操作,接口性能较低。

计划二:

利用分布式锁,强制串行化扣减同一商品库存。

该计划的 长处 是:

加重数据库压力,同时还能确保不会超卖。

该计划的 毛病 是:

每次只能有一个申请抢占锁,不能应答高并发场景。

对于惯例渠道,库存扣减是后置逻辑,流量不高,咱们采纳的是间接数据库扣减,且针对弊病做了一些 措施

  • 前置校验严格,同时针对刷单场景会有严格限流,保障最终扣减库存的流量可控;
  • 库存零碎读写拆散,缩小数据库的压力。

4.2.2 高并发库存扣减计划

针对高并发库存扣减,比方秒杀,个别采纳的是缓存扣减库存的形式(redis+lua 脚本实现单线程库存更新)作为前置流程,代替数据库间接更新。

在 redis 中扣减库存尽管性能高,能够大大加重数据库压力,但须要保障缓存数据能残缺、正确的入库,以保障最终一致性。

针对缓存数据更新至数据库,目前支流计划有两种:

计划一:Redis 数据间接异步更新至数据库。

长处:简略、没有简单的流程。

缺点:redis 宕机或者故障,可能会造成缓存内库存数据的失落。

计划二:Redis 扣减库存时,同步在业务数据中 insert 库存信息。

这里大家可能会有两个疑难:

  1. 有数据库的插入操作,性能怎么保障?
  2. 有数据库的操作,又有 redis 的更新,事务性怎么保障?
  3. 异步更新业务库存在提早,库存逆向回退如何保障?

对于疑难 1:因为数据库 insert 比 update 性能优,insert 是在表的开端直接插入,没有寻址的过程,能够保障性能比拟快。

对于疑难 2:计划 2 不同于缓存间接扣减,而是把缓存扣减放在数据库 insert 的事务内,通过数据库的事务保障整体的事务。

insert 的表被称为库存工作表,其中保留了库存扣减的信息,库存工作表构造能够设计的非常简单,主键 + 库存信息(json 字符串)就能够了。

后续通过异步工作,从库存工作表表中查问出库存更新信息,将其同步到具体的库存表中,实现最终一致性,这种计划能够防止数据的失落。

对于疑难 3:库存回退是依据业务库中扣减记录进行回退的,因为异步更新业务库必然存在提早(提早极低,数秒以内),所以极其场景会存在走退款逆向流程时业务库的库存扣减记录还未更新。

针对这种状况库存回退设置提早重试机制,如果再极其点达到重试阈值仍旧没有扣减记录,则返回回退胜利,不做阻断。

目前咱们针对秒杀库存扣减,采纳的是计划 2。但毕竟波及数据库的更新,为了防止危险,在前置流量校验上做了限度,保障流量的可控:

4.2.3 库存热点问题

什么是热点问题?热点问题就是因热点商品导致的 redis、数据库等性能瓶颈。在库存零碎中,热点问题次要存在

  • 采纳 间接扣减库存数据库 的形式,存在数据库的行锁问题。惯例渠道的库存扣减,咱们采纳的就是的就是这种形式。
  • 采纳 缓存扣减库存 的形式,大流量的状况下,热点商品扣减库存操作会打向 redis 单片,造成单片性能抖动,从而呈现 redis 性能瓶颈。

对于第 1 种热点问题,在 vivo 商城常见的场景是:新发的爆品手机,在准点售卖时会有抢购效应,容易造成库存数据库单行的瓶颈问题。针对这种热点问题,咱们的解决方案是“分而治之”:

对于潜在的热点爆款手机,咱们会将库存均匀分为多行(比方 M 行),扣减库存时,随机在 M 行中选取一行库存数据进行扣减。该计划冲破了数据库单行锁的瓶颈限度,解决了爆款商品的热点问题。

对于第 2 种 redis 单片热点问题,解决方案也是分而治之。将数据库中的库存数据同步到 redis 时,把 key 值打散,扩散在多个 redis 单片中。注:咱们目前线上的流量峰值还达不到会造成 redis 单片瓶颈的问题,为防止适度设计,只做了前置限流,没有进行 key 值的打散。

4.3 库存同步计划

库存零碎存在一些库存同步场景:

  • 对接仓储零碎,实现实物库存同步。
  • 兼容历史架构,商品零碎库存的可售库存同步等。

(1)实物库存同步:

实物库存同步,对接的是仓储零碎,通过接口来获取商品的理论库存。实物库存同步分成两种:定时全量同步、指定单品更新。

  • 定时全量同步:每天定时全量拉取库存调度平台的实物库存进行全量同步。
  • 制订单品:经营也能够手动触发单个 sku 的商品即时同步实物库存。

(2)商品零碎库存同步:

因为库存零碎多个场景波及库存变更,经营手动编辑、用户下单退款导致库存扣减回退,还有商品零碎内编辑库存数据也会导致库存变更(以前库存零碎未独立,库存数据保护在商品零碎)。同时很多业务在查问库存时,参考的仍旧是商品零碎的库存数据。

这里有一个问题:库存零碎曾经独立进去,为什么还会依赖商品零碎的库存数据?

这有两点 起因

  • 商城多个业务的后盾有商品筛选的须要,商品筛选会有库存数量的筛选项。商品数量很多,筛选是分页的,如果将库存数据全副替换成库存零碎的,那么存在跨零碎分页问题,分页筛选会存在问题;
  • 历史遗留问题,很多业务方依赖的是商品零碎的库存数据(包含依赖商品库存离线表的业务方),全副切换到库存零碎,老本和影响范畴大。

因而,咱们须要保障商品零碎和库存零碎两边库存数据的统一。

库存变更场景多,为了升高业务复杂度、采纳简略的形式实现库存同步,咱们利用了团队自研的 CDC 零碎(鲁班平台),整体流程如下:

库存数据库产生变更后,鲁班平台通过 binlog 采集获取库存变更日志,再通过自定义规定筛选,而后发送 mq 变更音讯,最初商品零碎生产音讯实现库存同步变更。

五、总结及瞻望

最初对库存零碎进行一个 总结

库存零碎实现服务拆分,在繁多的可售库存扣减性能根底上拓展了很多性能,赋能业务的倒退。

实现库存架构分层,形象多个库存类型,更灵便地满足以后业务需要。

针对库存扣减防重、高并发场景下的库存扣减、库存热点问题、库存同步等技术问题,咱们依据业务理论状况设计正当计划。

瞻望

目前 vivo 商城库存零碎平台化能力有余,局部能力扩散在其余零碎中,将来咱们心愿能为 vivo 新批发提供一体化的库存治理计划。

退出移动版