当前位置: 首页 > news >正文

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

背景介绍




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



接入商家同比增长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.lryc.cn/news/223432.html

相关文章:

  • 【redis】ssm项目整合redis,redis注解式缓存及应用场景,redis的击穿、穿透、雪崩的解决方案
  • chatGPT对英语论文怎么润色呢?
  • 【机器学习4】降维
  • 注册商标有助于企业拓展市场渠道
  • 推荐能用ios端磁力下载工具
  • 网页文档阅读的学习笔记
  • mysql图书管理系统(49-56)源代码
  • 使用Docker部署开源分布式任务调度系统DolphinScheduler
  • 光猫桥接与直接拨号的对比
  • 使用cpolar配合Plex搭建私人媒体站并实现远程访问
  • Web APIs——综合案例
  • 2023.10月考试战报|华为认证HCIP考试100%通过
  • Oracle 三种分页方法(rownum、offset和fetch、row_number() over())
  • 13. 一文快速学懂常用工具——Kubernetes 命令
  • 【Linux】shell执行文件清理
  • Android MotionLayout
  • 50基于matlab的传统滤波、Butterworth滤波、FIR、移动平均滤波、中值滤波、现代滤波、维纳滤波、自适应滤波、小波变换
  • 【2021研电赛】基于深度学习的蛋白质与化合物结合性质预测
  • 物联网中的毫米波雷达:连接未来的智能设备
  • 软件外包开发需求文档编写
  • Java算法(五):手写数组逆置API方法,实现数组逆置。 while实现 for循环实现
  • Vue el-table序号与复选框hover切换
  • 【Bug】Access to the path ‘xxx‘ is denied.
  • 郑州市管城回族区政协副主席张惠云一行莅临中创算力调研指导工作
  • SAE 2.0,让容器化应用开发更简单
  • Apache Storm 2.5.0 单机安装与配置
  • 4-爬虫-selenium(等待元素加载、元素操作、操作浏览器执行js、切换选项卡、前进后退异常处理)、xpath、动作链
  • 矩阵键盘独立接口设计(Keil+Proteus)
  • 国产猫罐头可以作为长期主食吗?口碑好的顶级猫罐头推荐
  • 大数据毕业设计选题推荐-营业厅营业效能监控平台-Hadoop-Spark-Hive