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

news2024/11/29 8:37:19

作者: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新零售提供一体化的库存管理方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/401983.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang的下载与安装

Windows系统 进入golang官方下载网站:所有版本 - Go 编程语言如图所示 下载后打开您下载的 MSI 文件,然后按照提示安装 Go。 验证是否已安装 Go。

【百宝书Linux】WSL-Windows中的Linux安装教程

大家好,我是涵子。今天我们来讲讲WSL。 配置环境: 一台装有Windows10及以上的电脑 较高性能的CPU 网络 目录 一、安装Ubuntu 二、安装WSL 三、运行WSL 四、补充 一、安装Ubuntu 首先,我们打开Microsoft Store。 搜索Ubuntu后&#xff0c…

容器方式搭建免费的表白网站--黑屏红心雪花飘零--背景音乐《三生三世》(2023.310更新)

效果图 拉取镜像 docker pull swr.cn-north-1.myhuaweicloud.com/loves/aixinbiaobai:20230310运行容器 docker run -di --name aixinbiaobai -e GIRLNAME=李华 -e BOYNAME=张三 -e STARTTIME=2023,2,10 -p 80:80 swr.cn-north-1.myhuaweicloud.com/loves/aixinbiaobai:2023…

【9】基础语法篇 - VL9 使用子模块实现三输入数的大小比较

VL9 使用子模块实现三输入数的大小比较 【报错】官方平台得背锅 官方平台是真的会搞事情,总是出一些平台上的莫名其妙的错误。 当然如果官方平台是故意考察我们的细心程度,那就当我没有说!! 在这个程序里,仿真时一直在报错 错误:无法在“test”中绑定wire/reg/memory“t…

0405习题总结-不定积分

文章目录1 不定积分的基本概念2 直接积分法-基本积分公式3 第一换元法-凑微分形式法4 第二类换元法5 分部积分求不定积分6 表格法积分7 有理函数求积分后记1 不定积分的基本概念 例1 f(x){x1,x≥012e−x12,x<0求∫f(x)dxf(x) \begin{cases} x1,\quad x\ge0\\ \frac{1}{2}e^…

【C++】register 关键字

文章目录一. 什么是寄存器&#xff1f;二. 为什么要存在寄存器&#xff1f;三. register 修饰变量一. 什么是寄存器&#xff1f; 我们都知道&#xff0c;CPU主要是负责进行计算的硬件单&#xff0c;但是为了方便运算&#xff0c;一般第一步需要先把数据从内存读取到CPU内&…

php设计模式-组合模式的运用

介绍 PHP的组合模式是一种设计模式&#xff0c;用于将对象组合成树形结构以表示“部分-整体”的层次结构。该模式允许客户端统一处理单个对象和组合对象&#xff0c;使得客户端在处理对象时不需要知道对象是否为单个对象还是组合对象。 在组合模式中&#xff0c;有两种类型的…

【零基础入门学习Python---Python的基本语法使用】

一.Python基本语法使用 Python是一种易学且功能强大的编程语言,具有简洁的语法和广泛的应用领域。在本文中,我们将介绍Python的基本语法使用,以帮助初学者快速入门Python编程。 1.1 注释 Python 支持两种类型的注释:单行注释和多行注释。 单行注释:以 # 符号开头,从 # …

ASEMI高压MOS管10N65参数,10N65规格,10N65封装

编辑-Z ASEMI高压MOS管10N65参数&#xff1a; 型号&#xff1a;10N65 漏极-源极电压&#xff08;VDS&#xff09;&#xff1a;650V 栅源电压&#xff08;VGS&#xff09;&#xff1a;30V 漏极电流&#xff08;ID&#xff09;&#xff1a;10A 功耗&#xff08;PD&#xff…

【QCY -T13 ANC】耳机使用(纯主观看法)

【QCY -T13 ANC】耳机测评主动降噪延迟不匹配耳机&#xff0c;手机本身延迟&#xff1a;235ms左耳机延迟&#xff1a;280ms右耳机延迟&#xff1a;274ms总结主动降噪 官方宣传28dB&#xff0c;使用的过程中确实可以达到降噪效果&#xff0c;但不细细品味根本没啥区别&#xff…

项目实战典型案例24——xxljob控制台不打印日志排查

xxljob控制台不打印日志排查一&#xff1a;背景介绍问题截图问题解读二&#xff1a;思路&方案三&#xff1a;过程四&#xff1a;总结一&#xff1a;背景介绍 本篇博客是对xxljob控制台不打印日志排查进行的总结和进行的改进。 目的是将经历转变为自己的经验。通过博客的方…

闪存驱动器怎么恢复数据?速速来get新妙招!

案例&#xff1a;闪存驱动器损坏了怎么办&#xff1f;里面的数据还可以恢复吗&#xff1f; “急急急&#xff01;&#xff01;&#xff01;前几天我在用U盘的时候&#xff0c;可能是由于不正确的插拔&#xff0c;或者是其他的原因&#xff0c;导致闪存驱动器损坏了。现在没有办…

windows电脑pc如何使用svn获取文档和代码

一、安装svn 下载链接 也可通过其他方式下载 二、使用 2.1 随便找一个文件夹 2.2 点击右键&#xff0c;选择SVN Checkout 2.3输入网址 如当你在网页上访问时地址为https://10.197.78.78/!/#aaa/view/head/bbb 在这里不能直接填入&#xff0c;而是 https://10.197.78.78/sv…

MySQL连接IDEA详细教程

使用IDEA的时候&#xff0c;需要连接Database&#xff0c;连接时遇到了一些小问题&#xff0c;下面记录一下操作流程以及遇到的问题的解决方法。 目录 MySQL连接IDEA详细教程 MySQL连接IDEA详细教程 打开idea&#xff0c;点击右侧的 Database 或者 选择 View --> Tool Wind…

安装了nodejs怎么安装nvm

第一步&#xff0c;从控制面板卸载已经安装的node 第二步&#xff0c;删除C盘program开头文件夹下的node文件 第三步&#xff0c;去C/user/用户名 文件夹下&#xff0c;删除.npmrc文件 第四步&#xff0c;打开隐藏文件&#xff0c;第三步文件夹下有一个Appdata文件&#xff…

JAVA进阶 —— Steam流

目录 一、 引言 二、 Stream流概述 三、Stream流的使用步骤 1. 获取Stream流 1.1 单列集合 1.2 双列集合 1.3 数组 1.4 零散数据 2. Stream流的中间方法 3. Stream流的终结方法 四、 练习 1. 数据过滤 2. 数据操作 - 按年龄筛选 3. 数据操作 - 演员信息要求…

mysql创建索引导致死锁,数据库崩溃,完美解决方案

文章目录写在前面一、短事务场景下&#xff0c;执行DDL语句场景分析1、短事务场景下&#xff0c;执行表字段添加操作2、短事务场景下&#xff0c;执行表字段修改操作3、短事务场景下&#xff0c;执行表字段删除操作&#xff08;1&#xff09;往里添加一条数据试试4、短事务场景…

操作系统内核与安全分析课程笔记【2】进程管理与调度

文章目录基本概念与关键数据结构进程管理进程生命周期进程的关系进程家族树线程组进程组与会话进程的创建与终止Linux中的线程基本概念与关键数据结构 进程&#xff1a;静态的&#xff0c;存储在磁盘上的代码与数据。 程序&#xff1a;动态的&#xff0c;执行程序的动态过程&am…

线程调度的基本过程

进程的基本调度过程 文章目录进程的基本调度过程一.什么是进程我们先用官方的话语去解释一下,大家先看我们对这个定义的一个结果如下:二.进程的特征三.进程的三种基本状态四.进程的管理4.1 什么是PCB4.2 PCB中的信息4.2.1 pid4.2.2 内存指针4.2.3文件描述符4.2.4 进程调度信息一…

【Android】aliyun云构建自动化打包

先贴出 阿里云移动研发平台EMAS-云构建的文档地址 一切以文档为主 简介 云构建服务支持通过流水线进行多端应用的编译构建任务&#xff0c;支持包签名、平台托管证书等能力&#xff0c;提升研发效率&#xff0c;规范研发流程。 提升研发效能&#xff0c;缩短交付周期 提升研发…