库存预占架构升级方案设计-交易库存中心

背景介绍




伴随物流行业的迅猛发展,一体化供应链模式的落地,对系统吞吐、系统稳定发出巨大挑战,库存作为供应链的重中之重表现更为明显。近三年数据可以看出:



接入商家同比增长37.64%、货品种类同比增长53.66%

货品数量同比增长46.43%、仓库数量同比增长18.87%

通过分析过往大促流量,分钟级流量增长率为75%,大促仓内反馈三方订单下传不及时,库存预占吞吐量和性能是导致订单积压因素之一。目前库存使用mysql数据库作为接单预占的扛量手段,随着一体化供应链建设以及重点KA商家不断接入,现有库存架构在业务支撑上存在风险和缺陷。

此外未来3到5年业务增长、流量增长预计增长5-10倍。为避免系统性能和技术架构缺陷导致业务损失,轻量级库存架构势在必行。



// 名词解释:

库存预占:是指消费者拍下商品订单后,库存先为该订单短暂预留,预留的库存即为预占库存。

架构原则




架构:是⾯向问题,解决问题的手段。 库存系统的问题: 非功能性:1.高并发 2.系统稳定性(容灾) 3.数据一致性 功能性: 1.业务复杂 2.数据一致性



系统设计




设计思路

1.当前库存系统瓶颈在哪里?

1.抗写流量,数据库成为瓶颈点。

2.如何解决系统瓶颈?

1.由高并发组件Redis替代数据库。

3.利用Redis需要解决哪些问题?

1.防超卖,异步写数据库保证最终一致性。









总体设计




扛量部分:库存性能瓶颈在预占,传统架构主要依靠数据库事务保持数据一致以及数据读写;新版架构设计将数据扛量部分移植到Redis,利用Redis高性能吞吐解决高并发场景下数据读写。

数据回写:Redis进行扛量削峰,后续数据仅用于记账,最终牺牲数据的短暂一致性达到削峰的目的。

差异部分:老版本库存预占设计仅依靠数据进行数据处理,新版设计依靠切量配置建数据切换到Redis,利用Redis高读写进行削峰操作。









详细设计




•主流程:







库存初始化:竞态条件利用Redis watch命令来实现锁等待,解决并发场景数据不一致问题。

LUA执行器:将原子操作指令/复用指令封装到LUA脚本中以减少网络开销。

补偿机制:i> 执行流程中所有业务异常发生时会同步发起反向操作请求;ii> 反向操作执行异常后会提交异步反向操作任务;iii>异步任务执行异常后,依赖监q控系统扫描异常单据或异常库存并修改异常库存量









回溯回写:任务落库后发出mq组装参数调用数据回写服务,数据回写服务操作库存数量;同时回写redis数据,释放预占量库存数据;更新任务库数据状态







数据结构




库存记录索引:{deptNo|goodsNo|warehouseNo}|stockStatus|stockType|goodsLevel

hashTag:{deptNo|goodsNo|warehouseNo}|stockStatus|stockType|goodsLevel

可售库存数量:usableKey:{库存记录索引}

扣减库存量:usableSubtractKey:{库存记录索引} ,记录Redis到DB执行期间减库存量

预占防重key:operateKey:{库存记录索引:单号} 防重key防并发重复请求

回滚防重:rollbackOperateKey:{库存记录索引}

缺量预占库存量:ullageOperateKey:{库存记录索引}

扣减库存单据记录:hSetrecord: {库存记录索引}





key预占缺量预占回滚回写
可售库存数量--+不变
扣减库存量++--
预占防重key++-不变
回滚防重不变不变+不变
缺量预占库存量不变+反向不变
扣减库存单据记录++--





Redis&DB






•首先进行redis&从库数据比对,若存在差异则对主库进行校验

•比对过程中,DB中sku明细行进行锁定(for update),比对逻辑为DB可用库存量==(Redis可用库存量+Redis预占量)

•有差异,报警且触发SDK可用量过期,同时矫正预占量







容灾方案












// 对系统容错/降级、监控机制(空间换稳定性,两份redis,故障3次丢数),流量分布材料,618流量大、峰值数据切量。数据不一致,多个商家,不能超过5分。



预占任务持久化:mysql需要将核心属性字段数据持久化:事业部,商品编码,仓编码,等级,库存类型,库存状态,预占库存量,任务状态;调度执行完成后需要更新stockTask状态为完成

初始化:

(1) lock db

(2) sum stockTask

(3)使用DB可用库存初始化Redis可用库存,stockTask预占量初始化Redis预占量

(4)Redis库存回滚,如果预占量key不存在,该key不需要回滚



性能结果










23年618大促











切量细则




切量细则



冷热数据




OMS库存冷热装置

预占架构升级切量重点key监控

库存预占架构升级切量商家

架构升级切量商家明细2

已切量商家



反向切量




原有设计中存在以下名单 禁止切量商家:优先级较高,一旦在名单中,禁止切量 批次库存商家:批次库存管理商家,目前该部分能力尚未建设 动态质押商家:物流金融业务,目前该部分能力尚未建设 切量名单商家:该部分为切量商家 原有切量流程:!禁止切量->!批次库存->!动态质押->切量名单中,通过以上校验为切量商家。 原有流程在增量商家中需要手动将商家配置到切量名单中才可进行切量操作,对于新增商家场景操作不变,且原有流程中逻辑库存名单为痛点:逻辑库存的启用配置在事业部主数据中,不在库存侧。 新版切量流程中对切量名单进行优化,将原来切量名单商家拆分成非逻辑库存名单、逻辑库存两个名单,其中: 非逻辑库存名单:包含可切量商家 逻辑库存名单:逻辑库存商家,该部分不可切量

原流程新流程对切量商家名单进行优化,拆分成非逻辑库存名单、逻辑库存两个名单

构建模型(批次库存&内存模型待续)










Redis存储数据结构

•MD生成规则工具集

◦逻辑库存MD5工具

     StringBuffer md5Key = new StringBuffer();md5Key.append(logicWarehouseStock.getGoodsNo()+"_"+logicWarehouseStock.getWarehouseNo()+"_"+logicWarehouseStock.getOwnerNo()+"_"+logicWarehouseStock.getDeptNo()+"_"+logicWarehouseStock.getStockType()+"_"+logicWarehouseStock.getGoodsLevel());if(StringUtils.isBlank(logicWarehouseStock.getFactor1())){md5Key.append("_0");}else {md5Key.append("_"+logicWarehouseStock.getFactor1());}if(StringUtils.isBlank(logicWarehouseStock.getFactor2())){md5Key.append("_0");}else {md5Key.append("_"+logicWarehouseStock.getFactor2());}if(StringUtils.isBlank(logicWarehouseStock.getFactor3())){md5Key.append("_0");}else {md5Key.append("_"+logicWarehouseStock.getFactor3());}if(StringUtils.isBlank(logicWarehouseStock.getFactor4())){md5Key.append("_0");}else {md5Key.append("_"+logicWarehouseStock.getFactor4());}if(logicWarehouseStock.getYn()== null){md5Key.append("_1");}else {md5Key.append("_"+logicWarehouseStock.getYn());}md5Key.toString().hashCode()

•批次库存MD5工具

public void fillMd5Value(){StringBuffer md5Key = new StringBuffer();md5Key.append(warehouseNo);md5Key.append("_");md5Key.append(goodsNo);md5Key.append("_");md5Key.append(goodsLevel);md5Key.append("_");md5Key.append(stockType);//遍历类字段不遍历map是为了控制MD5的组成顺序Class clazz = BatchAttrStock.class;Field[] fields = clazz.getDeclaredFields();try {int batchFieldCount = 0 ;for (Field field : fields){BatchAttrEnum attrEnum = BatchAttrEnum.batchFieldEnumMap.get(field.getName());//不是批属性的字段不进入MD5的组成if (attrEnum == null){continue;}batchFieldCount ++;field.setAccessible(true);Object value = field.get(this);if (value == null ){md5Key.append("0");continue;}if(field.getType().toString().contains("String")){md5Key.append(value);continue;}if(field.getType().toString().contains("Date")){Date timeField = (Date) value;md5Key.append(timeField.getTime());continue;}throw new RuntimeException(attrEnum.getField()+"填充MD5异常");}//默认50个批属性长度,长度不够0补齐int remainLength = 50 - batchFieldCount;String str = String.format("%0"+remainLength+"d", 0);md5Key.append(str);}catch (Exception e){throw new RuntimeException("填充MD5异常.");}md5Key.append(yn);String md5Value =  MD5Util.md5(md5Key.toString());setMd5Value(md5Value);}

•MD&ID&属性保存工具

待续。。。。

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

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

相关文章

CLIP:万物分类(视觉语言大模型)

本文来着公众号“AI大道理” ​ 论文地址:https://arxiv.org/abs/2103.00020 传统的分类模型需要先验的定义固定的类别,然后经过CNN提取特征,经过softmax进行分类。然而这种模式有个致命的缺点,那就是想加入新的一类就得重新定义…

零基础快速上手STM32开发(手把手保姆级教程)

零基础快速上手STM32开发(手把手保姆级教程) 1. 前言 作为一名嵌入式工程师,STM32 是必须要学习的一款单片机,同时这款单片机资料足够多,而且比较简单,非常适合初学者入门。 STM32 是一款由 STMicroelec…

双H桥直流马达步进电机驱动芯片SS8833E

由工采网代理的率能SS8833E是一款适用于有刷直流或双极步进电机的集成电机驱动芯片;采用eTSSOP16封装;该器件集成了两个PNMOS H桥和电流调节电路;电机输出电流可以由外部脉宽调制器(PWM)或内部PWM电流控制器控制。 工…

【Java 进阶篇】JQuery DOM操作:通用属性操作的绝妙魔法

在前端的舞台上,JQuery犹如一位魔法师,为我们展现了操纵HTML元素的奇妙技巧。而在这个技巧的精妙组成中,通用属性操作是一门绝妙的魔法。在本篇博客中,我们将深入研究JQuery DOM操作中的通用属性操作,揭示这段魔法的神…

11.13 牛客刷题8/10

11.13 信号完整性 指针地址 的加减,注意 最后转为16进制

leetCode 25.K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。k 是一个正整数,它的值小于 或 等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。你不能只是单纯的改变节点内部的值&a…

【PIE-Engine 数据资源】全球250米LAI产品

文章目录 一、 简介二、描述三、波段四、示例代码参考资料 一、 简介 数据名称全球250米LAI产品时间范围2015年空间范围全球数据来源北京师范大学肖志强教授团队代码片段var images pie.ImageCollection(“BNU/LAI/GLOBAL-250”) 二、描述 全球 250 米叶面指数产品由北京师范…

本地PHP搭建简单Imagewheel私人云图床,在外远程访问——“cpolar内网穿透”

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道(云端设置)3.3.Cpolar稳定隧道(本地设置) 4.公网访问测…

产品化的GPT,能否为“百模大战”照亮未来?

这两天,AI圈都处在一种莫名的震撼感当中。 北京时间 11月7日,OpenAI 举办了首次DevDay开发者日活动。活动现场发布了非常多内容,其中有一些按部就班的,比如技术上更新了最新版本的GPT-4 Turbo。也有一些让从业者目瞪口呆&#xff…

高速高精运动控制,富唯智能AI边缘控制器助力自动化行业变革

随着工业大数据时代的到来,传统控制与决策方式无法满足现代数字化工厂对工业大数据分析与决策的需求,AI边缘控制器赋能现代化智慧工厂,实现工业智造与行业变革。 富唯智能AI边缘控制器,基于x86架构的IPC形态产品,通过…

python爬虫hook定位技巧、反调试技巧、常用辅助工具

一、浏览器调试面板介绍 二、hook定位、反调试 Hook 是一种钩子技术,在系统没有调用函数之前,钩子程序就先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,也可以强制结束消息的传递。简单…

视觉大模型DINOv2:自我监督学习的新领域

1 DINOv2 1.1 DINOv2特点 前段时间,Meta AI 高调发布了 Segment Anything(SAM),SAM 以交互式方式快速生成 Mask,并可以对从未训练过的图片进行精准分割,可以根据文字提示或使用者点击进而圈出图像中的特定…

【Git】第四篇:基本操作(理解工作区、暂存区、版本库)

Git 工作区、暂存区和版本库 工作区:就是我们创建的本地仓库所在的目录暂存区: stage或index,一般放在.git(可隐藏文件)目录下的index文件(.git/index)中,所以我们把暂存区有时候也叫做索引(in…

JVM:如果是你,你如何解决跨代引用的问题?(记忆集和卡集)

这部分内容主要是为了稍后介绍各款垃圾收集器时做前置知识铺垫,如果对这部分内容感到枯燥或者疑惑,可以先放下看,等后续遇到要使用它们的实际场景、实际问题时再结合问题,再回来阅读和理解。 记忆集和卡集 前面在分代收集理论那…

【Java 进阶篇】JQuery DOM操作:舞动网页的属性魔法

在前端的舞台上,属性操作是我们与HTML元素进行互动的关键步骤之一。而JQuery,这位前端开发的巫师,通过简洁而强大的语法,为我们提供了便捷的属性操作工具。在这篇博客中,我们将深入研究JQuery DOM操作中的属性操作&…

FPGA与STM32_FSMC总线通信实验

FPGA与STM32_FSMC总线通信实验 内部存储器IP核的参数设置创建IP核FPGA代码STM32标准库的程序 STM32F407 上自带 FSMC 控制器,通过 FSMC 总线的地址复用模式实现STM32 与 FPGA 之间的通信,FPGA 内部建立 RAM 块,FPGA 桥接 STM32 和 RAM 块&…

【算法 | 模拟No.5】leetcode 74. 搜索二维矩阵

个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…

抢抓泛娱乐社交出海新风口!Flat Ads深圳沙龙活动引爆海外市场

随着全球化进程的加速,中国的应用类APP不断走向国际市场。作为产品和服务的提供者,中国开发者围绕社交泛娱乐创新,开启直播出海、短视频出海、游戏社交出海、1V1 视频出海、音频社交出海等出海热潮。“社交、泛娱乐”融合成为行业主流发展趋势…

Go的优雅退出

Go优雅退出/停机以前主要通过signal来实现,当然现在也是通过signal来实现,只是从go 1.16开始,新增了更加友好的API: func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) 该…

DevChat:开发者专属的基于IDE插件化编程协助工具

DevChat:开发者专属的基于IDE插件化编程协助工具 一、DevChat 的介绍1.1 DevChat 简介1.2 DevChat 优势 二、DevChat 在 VSCode 上的使用2.1 安装 DevChat2.2 注册 DevChat2.3 使用 DevChat 三、DevChat 的实战四、总结 一、DevChat 的介绍 在AI浪潮的席卷下&#x…