网站开发自学,平和网站建设,wordpress编辑器增强,商城类app制作价格背景介绍 #xfeff;
伴随物流行业的迅猛发展#xff0c;一体化供应链模式的落地#xff0c;对系统吞吐、系统稳定发出巨大挑战#xff0c;库存作为供应链的重中之重表现更为明显。近三年数据可以看出#xff1a; #xfeff;#xfeff;
接入商家同比增长37.64%、货…背景介绍 伴随物流行业的迅猛发展一体化供应链模式的落地对系统吞吐、系统稳定发出巨大挑战库存作为供应链的重中之重表现更为明显。近三年数据可以看出 接入商家同比增长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执行期间减库存量
•预占防重keyoperateKey{库存记录索引单号} 防重key防并发重复请求
•回滚防重rollbackOperateKey{库存记录索引}
•缺量预占库存量ullageOperateKey{库存记录索引}
•扣减库存单据记录hSetrecord: {库存记录索引} key预占缺量预占回滚回写可售库存数量--不变扣减库存量--预占防重key-不变回滚防重不变不变不变缺量预占库存量不变反向不变扣减库存单据记录-- RedisDB •首先进行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预占量
4Redis库存回滚如果预占量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(%0remainLengthd, 0);md5Key.append(str);}catch (Exception e){throw new RuntimeException(填充MD5异常.);}md5Key.append(yn);String md5Value MD5Util.md5(md5Key.toString());setMd5Value(md5Value);}
•MDID属性保存工具
待续。。。。