摘要:库存平台为货品流通链路提供全面的库存管理服务,贯穿其整个订单生命周期,是电商领域不可或缺的核心模块。在平台建设过程中,我们面临了诸多稳定性方面的挑战。
本文总结库存平台稳定性建设中遇到的问题以及解决方案。感谢【金鹏】、【孙静】、【陈瑞】同学在本文撰写中提供的内容及帮助!
库存平台为货品流通链路提供全面的库存管理服务,贯穿其整个订单生命周期,是电商领域不可或缺的核心模块。在平台建设过程中,我们面临了诸多稳定性方面的挑战。
具体而言,存在以下问题:
1、业务流程繁多,不同流程共同访问同一应用,容易相互影响。例如,销售单库存预占功能出现问题时,退供预占功能亦随之失效。
2、业务流程复杂,修改易出错。销售单预占等流程,流程分支多、步骤多,新入职的员工需要至少一周的时间才能理解这些流程。对如此复杂的流程进行修改极易导致错误。
3、库存数据的准确性要求极高。一旦某一货品的库存信息出现错误,可能导致该货品后续的订单都无法正常下单或无法履约。
4、数据库热点操作带来的稳定性威胁。秒杀活动、直播促销等业务场景,往往会出现短时间内多个订单都去预占某一个或几个商品库存的情况。多个线程并发对同一个数据库商品数据做库存扣减时,数据库中会加锁来保障数据被正确操作。当商品数据足够【热】时,大量的锁等待会引发性能问题,进而导致系统不可用,见下图:
5、店铺库存高频、大规模计算带来的稳定性威胁。一个商家的库存发生变动后,需要把商家库存重新分配给商家名下的各个店铺,这个分配过程计算量极大,容易把CPU打满,进而影响所有服务
为了应对库存平台所面临的稳定性挑战,除了通用的可用率可视化、限流等治理措施外,我们还结合库存业务的特点,采取了以下稳定性建设。
我们识别到,库存流量有以下几个特点:
1、存在必须要重点保障的核心流量。销售单库存预占能力属于物流核心流程,其目的是为京东物流仓配业务提供出库单库存校验服务,从而避免订单量超出库存容量而错误地传递到仓库管理系统(WMS)中,在接单链路中若库存操作失败则订单接单失败,造成公司大量的财产损失
2、存在一些计算量大又不需要实时处理的流量。如WMS出库以后,需要回传库存出库情况,这部分流量很高,但对处理实时性要求不高
3、存在一些明细行特别多的库存批量操作。以销售单库存预占为例,绝大部分库存操作批量大小在10个以内,但是也存在部分批量大小超过6000的情况。不同的批量大小,调用方需要有不同的超时配置,如批量大小在10个以内,超时时间配置为500毫秒;批量大小大于6000时,超时时间需要配置为50秒
基于流量的这些特点,我们对库存流量拆分如下:
注:流量拆分使用不同的服务分组实现
针对库存操作流程复杂且改动容易出错的情况,我们在过往的上线过程中,通常会使用ducc编写一个流程开关,逐步进行灰度放量。
然而,这种做法存在两个主要问题:
1、维护成本较高。需求经过验证后,仍需再次上线以移除不再使用的开关,但往往因疏忽而未能及时下线,导致代码中充斥着冗余的开关,逻辑变得混乱。
2、容易引发线上问题。大型需求的改动点通常较多,需要在多个地方添加开关,稍有不慎便可能遗漏,从而导致线上问题频发。
我们识别到,库存业务通常在一个商家范围内进行。因此,库存操作的核心接口均包含以商家为单位的参数。在进行灰度验证时,可以按商家单位进行灰度切量。基于这一特性,库存系统构建了以商家为基础的灰度链路。
相较于以往的灰度方法,灰度链路无需在每次需求变更时增加流程控制代码,从而降低了线上出现问题的概率。
对于某一货品的库存操作,将变更一条或多条库存记录。例如,在依据渠道优先级进行出库操作时,将优先扣减京东渠道的库存,再扣减共享渠道的库存。当某一货品的操作涉及多条库存记录时,需为每条库存记录分配相应的操作数量。操作完成后,应为每条记录生成变更记录流水。
当商家出售爆款商品时,会频繁扣减特定库存记录。在操作数据库的过程中,数据库会对库存记录进行加锁,并按顺序执行这些扣减操作。这导致容器中积累了大量等待的线程和数据库连接,一方面可能导致服务请求超时,另一方面,大量占用容器资源可能引发容器宕机,进而影响其他商品的库存操作。
倘若采取粗暴的限流措施,将导致大量库存扣减失败,并且连非热门商品也会受到波及,造成不良影响。
我们的初版的解决方案是在缓存中添加库存数据,利用缓存的高效性能来应对热点问题。我们按商家的粒度逐步在灰度链路中进行流量切换,具体如下图所示。:
通过此项优化,成功将热点商品预占TPS从50提升到1200,提升了24倍。TP99降低到130ms,降低至原时长的4.3%(从3000ms到130ms)。
橙色部分为优化后的结果:
在切量过程中,我们发现了两个问题:
1、数据库与Redis数据存在不一致的情况。由于某些业务场景考虑不周全,导致数据库与Redis数据出现不一致,而这些问题往往需要通过商家或下游的反馈才能被发现并修复。
2、部分关键客户商家难以进行灰度切量。一些大型关键客户商家在处理库存时,存在定制化的特殊处理逻辑,使用缓存来支撑这些逻辑的成本较高且容易出错。
操作流程上面临的主要技术问题如下图:
•初始化流程方案。使用 锁定db库存+redis事务来保障数据一致性。见下图:
•数据同步流程。使用mq重试+任务系统兜底来保障同步能完成
库存平台每日执行数百万次库存操作,如何在不影响系统性能的同时及时发现数据不一致的情况,是一个需要仔细权衡的问题。我们采取了以下解决方案:
增加缓存流水
当发现有数据库-redis数据不一致时,我们需要有数据来协助我们定位不一致问题发生的原因,所以在es中增加了redis缓存操作的流水记录,如下图:
我们开发了管理页面,支持以商家、货品、单据等维度来查询并操作数据库、redis数据,界面如下图:
一些大型关键客户商家在处理库存时,存在大量定制化的逻辑,在缓存中实现一次不仅成本高,还容易出现错漏。针对这部分商家,我们采用了以下治理思路。
异步限流设计思路:让热点不那么热。通过减缓预占操作速度,从而降低热点热度,缓解库存系统的性能压力。见下图:
热点检测、限流
设计思路:及时识别热点库存,对热点库存实施流量控制,从而确保系统的稳定性。
流量控制采用滑动窗口算法。实现原理:每当经过一个时间点,时间窗口即向前推进一个单位,计算该窗口内的请求数量。若请求数量超出预设限制,则拒绝处理请求;反之,则予以处理。
具体实现使用AOP切面编程,流程如下图:
注:限流流程在try-catch中执行,中间除了【被限流异常】,别的异常都捕获吞掉,防止影响正常逻辑
热点限流告警示例:
商家通过入驻京仓来管理其线上与线下销售渠道的库存。京仓是一个综合性的仓储管理系统,它帮助商家有效地整合和管理其库存资源。商家可以在京东POP店、京东自营VMI、唯品会、淘天、抖音、快手等线上平台以及线下门店进行销售。这种多渠道销售模式不仅扩大了商家的市场覆盖面,还提高了销售效率。
为了防止超卖现象,商家需要确保线上与线下库存的共享。这意味着商家必须实时更新库存数据,确保所有销售渠道都能看到最新的库存信息。通过这种方式,商家可以避免因库存信息不同步而导致的超卖问题,从而提升客户满意度和信任度。例如,如果一个商品在线上被售出,系统会立即更新库存信息,确保线下门店不会再次销售同一商品。
目前,已有75,821家商家入驻京仓,店铺数量达到162,306家,实物1,785万件,明细行数为9,309万条。这些数据表明,京仓已经成为众多商家管理库存的重要平台。
随着商家接入量、计算场景的增加,系统计算量也随之增大,常常使得容器CPU和数据库CPU的负载过高,进而影响整个系统的稳定性且都是后知后觉,无法提前控量。当容器CPU的负载过高时,会导致对外提供的JSF服务的可用率降低。这不仅影响了用户体验,还可能导致商家的业务受到影响,为了保持系统的稳定性和性能,需要对系统进行优化和升级,同时加强监控和维护,确保系统能够高效地运行。
前置动作
1、梳理出所有触发计算的场景共25个触发点库存变化、低于安全水位、调整商品比例、调整店铺维度比例、虚拟组套修改、固定值设置等。
2、从以上场景中又梳理出“杀伤力”最大的两个计算场景分别是
店铺比例调整:整个事业部下的所有SKU全量计算。
虚拟组套计算:由于虚拟组套规则的特殊性,单次计算会生成1.5W+条计算流水。
我们对店铺维度比例调整场景,进行了提前发现并对该事业部进行提前限流,有效的解决了流量一下全打到应用服务器,导致CPU飙升的场景
效果:
通过日常OPS Review发现,当计算任务量激增时,整个系统的资源会被大量占用,导致对外服务响应时间变长,甚至出现服务中断的情况。为了改善这一状况,我们决定将计算功能和JSF功能进行分组隔离。这样,计算任务和用户界面操作可以分别在独立的资源池中运行,互不影响。
效果
优化前:
优化后
虚拟组套专为京仓来源的订单设计,通过在ECLP中配置虚拟组套关系,实现JD商城下单时为一个套装SKU,而ECLP接单及WMS出库时则按原料SKU出库的功能,给商家带来了更灵活的销售策略。
例如,组套SKU=1*原料1(20%)+2*原料2(20%)+1*原料3(20%)。通过组套关系可以看出,组套SKU的库存量来源于原料。在进行组套计算时,需先根据店铺维度比例或EMG分配比例计算出原料库存,再依据组套规则分配给组套SKU,需进行二次计算。实际应用中,每个原料可以共享给多个组套,整个绑定关系呈现网状结构,单次计算量会成倍增加(1W+),占用更多计算资源,从而影响其他非组套商家的计算。
计算示例:
通过以上计算MQ拆分,并通过JMQ4进行限流,可以做到平滑计算,不会再出现重算量激增导致CPU一下打满情况。
优化前:
优化后:
当前库存平台的监控告警主要集中在接口层面,然而,接口的成功返回并不完全意味着业务处理已正常完成并落库。因此,我们计划引入更为全面的数据维度监控告警机制,以确保业务处理的完整性和准确性。
监控策略:以小时数据为单位进行对比,例如将2月28日9:00至10:00期间的预占成功与回传成功数量,与2月21日同一时间段的数据进行对比。当差异比例超过预设阈值时,触发告警。
告警触发条件:定时触发,每小时的第五分钟执行一次,例如在10:05触发,对比9:00至10:00的数据。
数据来源:通过查询ES或大数据平台进行对比,以避免对线上核心业务造成影响。
当前接收到数据库与Redis不一致的告警后,我们采取人工查询数据库流水及Redis流水并进行比对的方式,以查找不一致的原因。由于一个货品在特定时间段内的流水记录可能极为繁多,这一比对过程耗时且费力。为此,我们计划开发一款不一致比对工具,实现自动化分析不一致的原因,并输出导致不一致的具体单据。
来源:京东云开发者