伴随物流行业的迅猛发展,一体化供应链模式的落地,对系统吞吐、系统稳定发出巨大挑战,库存作为供应链的重中之重表现更为明显。近三年数据可以看出:
接入商家同比增长37.64%、货品种类同比增长53.66%
货品数量同比增长46.43%、仓库数量同比增长18.87%
通过分析过往大促流量,分钟级流量增长率为75%,大促仓内反馈三方订单下传不及时,库存预占吞吐量和性能是导致订单积压因素之一。目前库存使用mysql数据库作为接单预占的扛量手段,随着一体化供应链建设以及重点KA商家不断接入,现有库存架构在业务支撑上存在风险和缺陷。
此外未来3到5年业务增长、流量增长预计增长5-10倍。为避免系统性能和技术架构缺陷导致业务损失,轻量级库存架构势在必行。?
//名词解释:
库存预占:是指消费者拍下商品订单后,库存先为该订单短暂预留,预留的库存即为预占库存。
架构原则架构:是?向问题,解决问题的手段。库存系统的问题:非功能性:1.高并发2.系统稳定性(容灾)3.数据一致性功能性:1.业务复杂2.数据一致性
系统设计设计思路1.当前库存系统瓶颈在哪里?抗写流量,数据库成为瓶颈点。
2.如何解决系统瓶颈?由高并发组件Rdis替代数据库。
3.利用Rdis需要解决哪些问题?防超卖,异步写数据库保证最终一致性。
总体设计扛量部分:库存性能瓶颈在预占,传统架构主要依靠数据库事务保持数据一致以及数据读写;新版架构设计将数据扛量部分移植到Rdis,利用Rdis高性能吞吐解决高并发场景下数据读写数据回写:Rdis进行扛量削峰,后续数据仅用于记账,最终牺牲数据的短暂一致性达到削峰的目的。差异部分:老版本库存预占设计仅依靠数据进行数据处理,新版设计依靠切量配置建数据切换到Rdis,利用Rdis高读写进行削峰操作。??详细设计主流程:??库存初始化:竞态条件利用Rdiswatch命令来实现锁等待,解决并发场景数据不一致问题。LUA执行器:将原子操作指令/复用指令封装到LUA脚本中以减少网络开销。补偿机制:i执行流程中所有业务异常发生时会同步发起反向操作请求;ii反向操作执行异常后会提交异步反向操作任务;iii异步任务执行异常后,依赖监q控系统扫描异常单据或异常库存并修改异常库存量??回溯回写:任务落库后发出mq组装参数调用数据回写服务,数据回写服务操作库存数量;同时回写dis数据,释放预占量库存数据;更新任务库数据状态??数据结构库存记录索引:{dptNogoodsNo
wahousNo}
stockStatus
stockTyp
goodsLvlhashTag:{dptNo
goodsNo
wahousNo}
stockStatus
stockTyp
goodsLvl可售库存数量:usablKy:{库存记录索引}扣减库存量:usablSubtractKy:{库存记录索引},记录Rdis到DB执行期间减库存量预占防重ky:opratKy:{库存记录索引:单号}防重ky防并发重复请求回滚防重:rollbackOpratKy:{库存记录索引}缺量预占库存量:ullagOpratKy:{库存记录索引}扣减库存单据记录:hStcord:{库存记录索引}?RdisDB首先进行dis从库数据比对,若存在差异则对主库进行校验比对过程中,DB中sku明细行进行锁定(forupdat),比对逻辑为DB可用库存量==(Rdis可用库存量+Rdis预占量)有差异,报警且触发SDK可用量过期,同时矫正预占量??容灾方案?
??//对系统容错/降级、监控机制(空间换稳定性,两份dis,故障3次丢数),流量分布材料,流量大、峰值数据切量。数据不一致,多个商家,不能超过5分。
预占任务持久化:mysql需要将核心属性字段数据持久化:事业部,商品编码,仓编码,等级,库存类型,库存状态,预占库存量,任务状态;调度执行完成后需要更新stockTask状态为完成
初始化:
(1)lockdb
(2)sumstockTask
(3)使用DB可用库存初始化Rdis可用库存,stockTask预占量初始化Rdis预占量
(4)Rdis库存回滚,如果预占量ky不存在,该ky不需要回滚
性能结果???23年大促
??切量细则?切量细则?
冷热数据?OMS库存冷热装置?
?预占架构升级切量重点ky监控?
?库存预占架构升级切量商家?
?架构升级切量商家明细2?
?已切量商家?
反向切量原有设计中存在以下名单禁止切量商家:优先级较高,一旦在名单中,禁止切量批次库存商家:批次库存管理商家,目前该部分能力尚未建设动态质押商家:物流金融业务,目前该部分能力尚未建设切量名单商家:该部分为切量商家原有切量流程:!禁止切量-!批次库存-!动态质押-切量名单中,通过以上校验为切量商家。原有流程在增量商家中需要手动将商家配置到切量名单中才可进行切量操作,对于新增商家场景操作不变,且原有流程中逻辑库存名单为痛点:逻辑库存的启用配置在事业部主数据中,不在库存侧。新版切量流程中对切量名单进行优化,将原来切量名单商家拆分成非逻辑库存名单、逻辑库存两个名单,其中:非逻辑库存名单:包含可切量商家逻辑库存名单:逻辑库存商家,该部分不可切量
原流程新流程对切量商家名单进行优化,拆分成非逻辑库存名单、逻辑库存两个名单
构建模型(批次库存内存模型待续)?Rdis存储数据结构MD生成规则工具集?逻辑库存MD5工具
StringBuffrmd5Ky=nwStringBuffr();md5Ky.appnd(logicWahousStock.gtGoodsNo()+"_"+logicWahousStock.gtWahousNo()+"_"+logicWahousStock.gtOwnrNo()+"_"+logicWahousStock.gtDptNo()+"_"+logicWahousStock.gtStockTyp()+"_"+logicWahousStock.gtGoodsLvl());if(StringUtils.isBlank(logicWahousStock.gtFactor1())){md5Ky.appnd("_0");}ls{md5Ky.appnd("_"+logicWahousStock.gtFactor1());}if(StringUtils.isBlank(logicWahousStock.gtFactor2())){md5Ky.appnd("_0");}ls{md5Ky.appnd("_"+logicWahousStock.gtFactor2());}if(StringUtils.isBlank(logicWahousStock.gtFactor3())){md5Ky.appnd("_0");}ls{md5Ky.appnd("_"+logicWahousStock.gtFactor3());}if(StringUtils.isBlank(logicWahousStock.gtFactor4())){md5Ky.appnd("_0");}ls{md5Ky.appnd("_"+logicWahousStock.gtFactor4());}if(logicWahousStock.gtYn()==null){md5Ky.appnd("_1");}ls{md5Ky.appnd("_"+logicWahousStock.gtYn());}md5Ky.toString().hashCod()
批次库存MD5工具publicvoidfillMd5Valu(){StringBuffrmd5Ky=nwStringBuffr();md5Ky.appnd(wahousNo);md5Ky.appnd("_");md5Ky.appnd(goodsNo);md5Ky.appnd("_");md5Ky.appnd(goodsLvl);md5Ky.appnd("_");md5Ky.appnd(stockTyp);//遍历类字段不遍历map是为了控制MD5的组成顺序Classclazz=BatchAttrStock.class;Fild[]filds=clazz.gtDcladFilds();try{intbatchFildCount=0;for(Fildfild:filds){BatchAttrEnumattrEnum=BatchAttrEnum.batchFildEnumMap.gt(fild.gtNam());//不是批属性的字段不进入MD5的组成if(attrEnum==null){continu;}batchFildCount++;fild.stAccssibl(tru);Objctvalu=fild.gt(this);if(valu==null){md5Ky.appnd("0");continu;}if(fild.gtTyp().toString().contains("String")){md5Ky.appnd(valu);continu;}if(fild.gtTyp().toString().contains("Dat")){DattimFild=(Dat)valu;md5Ky.appnd(timFild.gtTim());continu;}thrownwRuntimExcption(attrEnum.gtFild()+"填充MD5异常");}//默认50个批属性长度,长度不够0补齐intmainLngth=50-batchFildCount;Stringstr=String.format("%0"+mainLngth+"d",0);md5Ky.appnd(str);}catch(Excption){thrownwRuntimExcption("填充MD5异常.");}md5Ky.appnd(yn);Stringmd5Valu=MD5Util.md5(md5Ky.toString());stMd5Valu(md5Valu);}
MDID属性保存工具本文篇幅有限,余下二期进行分享。
作者:京东物流金鹏
来源:京东云开发者社区自猿其说Tch转载请注明来源