您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
repromise系统资源收敛与核心服务优化实践
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
repromise系统资源收敛与核心服务优化实践
自猿其说Tech
2022-04-07
IP归属:未知
576480浏览
计算机编程
### 1 前言 在订单全流程过程中,各个环节存在因为各种情况导致订单时效出现异常(如转移、预分拣、配送违约等)、时效过长(非现货转成现货、偏远地区、POPsop订单长时效等)或订单无法达成(商品质量、包装破损、缺少配件、第三方退货等),无法正常执行订单,造成订单违约或无法履约的情况。因对订单异常管理不完善,时常出现客户催单、投诉、晒单抱怨或宣扬京东管理的负面行为。因此需要对各环节异常订单,重新给出送达时效,方便客服快速解决客户反馈的问题,同时客户针对异常订单及时在跟踪中了解最新进度。 ### 2 业务场景 1. 集群存在大量读写场景。 2. 大宽表,将订单全流程相关环节数据做实时监控,数据表都是通过Flink加工出来的宽表,针对仓储、分拣、运输和终端环节的异常订单重新计算时效,给出各环节最新预计完成时间,并推送安慰话术至订单全流程。 3. Flink中数据加工实时追加写入,worker在5分钟一次异常计算结果更新。 4. 为商城催单业务提供根据订单号查询repromise时效高性能接口(要求700MS必须返回结果,超时则催单业务由节机器人回复转为人工客服跟进处理,增加人工客服压力)。 ### 3 遇到的问题 #### 3.1系统问题 ##### 3.1.1 核心服务性能差 11.11期间,随着单量上涨,整体催单业务量不断攀升,接口调用量较平时提高2.5倍,接口性能急剧恶化,UMP关键参数TP99:4871ms MAX:39999ms AVG: 963ms,导致上游接口可用率持续下降,无法保证上游正常业务。 ![](//img1.jcloudcs.com/developer.jdcloud.com/587d7d60-95ba-4562-936b-051d9767843f20220407140540.png) ###### 1.问题分析结果 - 垃圾数据未归档,导致无效数据占用大量空间。 - ES集群物理机老化,老化机器性能存在瓶颈,集群无法有效merge数据,剔除无效数据,导致查询扫描数据量大,增加集群开销。 - 接口查询、worker执行整体效率不高,需跟进代码优化。 ###### 2.如何发现物理机老化问题? - 对ES集群进行综合分析和排查,排查过程中发现,ES集群CPU彪高只出现在固定的几台机器,同时索引文档从33亿结转至剩余7亿,一段时间以后,索引占用空间没有明显减少,反而有小幅提升,主分片大小由原来的1.7T升至2T,以上种种现象无法解释; - 排查接口调用的地方并没有存在慢查询,集群慢查询来自worker任务,worker查询的频率很低,因此对接口的影响不至于产生大量跳点影响。 - 拉通ES同事,一起排查;首先排查慢查询影响,通过长时间排查,慢查询不会一直影响接口查询性能;于是排查ES集群网关到数据节点、应用到ES集群网关网络情况,发现网络影响较小,初步断定机器存在影响;于是进行机器更换。 - 将原集群24个数据节点进行机器更换,缩减至16个数据节点;更换完成以后,数据进行全部重新merge,最后索引主分片大小数据占用仅为489G,与调整前2T相比,有大幅空间缩减,很明显集群的merge存在问题;进一步观察接口性能,有了明显提升,TP99维持在1秒以内,平均请求在200ms左右,但是还存在少量跳点,需要后期跟进持续优化。 ##### 3.1.2 遗漏单据 worker任务异常单据在履约计算偶尔丢失一单未计算,导致偶尔一单遗漏处理。 ##### 3.2 客观问题 系统刚交接到手,整体系统及业务熟悉需要一个过程。 ### 4 优化方案 #### 4.1 数据清洗 问题排查过程中发现核心索引20天数据总量达33亿左右,根据业务实际评估有效数据量应维持在7亿左右,需要我们弄清楚剩余26亿数据形态;分析发现,数据形态如下:1、历史数据(几年前的) 2、无关键有效指标字段数据。从分析结果来看,这部分数据无实际用途,可视为垃圾数据。刨根问底,这部分数据是如何产生的?今后如何避免类似的问题继续发生? **根因分析:** 在Flink数据加工环节,大宽表是通过监控多张业务表binlog日志组合加工而成,监控时效为20天,20天有效监控期完成,主表数据进行结转。实际情况下,主表结转后,其他业务表数据更新后则数据又一次被加工进入索引,造成此类垃圾数据一直堆积。同时数据加工环节,只针对主表数据增加TS标记,导致这部分数据无法按照TS标识进行结转。 **数据清洗方案:** - 清理索引历史垃圾数据,将索引数据从33亿降至7亿。 - Flink数据加工时所有业务表增加TS结转标记,对于历史数据按照标识定期结转。 - Flink数据加工环节过滤无效过期数据,源头上屏蔽垃圾数据的产生,同时避免无效数据存储后在结转多重资源浪费的情况发生,节省JRC计算资源。 - 集群其他索引采取同样举措,最大力度优化缩减集群存储资源。 #### 4.2 接口优化 接口根据订单号查询最新履约时效,通过业务分析来看,一次查询只获取一条数据。分析优化前代码发现,当前接口查询采用query查询方案(Query查询执行顺序如下文),每一次查询都需要从集群所有数据节点上去匹配结果,结合接口查询业务诉求来看,实际数据只存储在某一个数据节点上,其他的数据节点计算纯属资源浪费。有没有一种方案可以定向到对应的数据节点直接查询?从而避免集群计算资源浪费。 **知识点:Query查询执行顺序** - 查询请求打到协调节点,协调节点发送请求到shard分布的数据节点,请求数据。 - 每个分片在本地执行查询,并使用本地的Term/Document Frequency信息进行打分,添加结果到大小为from + size的本地有序优先队列中。 - 每个分片返回个自己优先队列中所有文档的ID和排序值给协调节点,协调节点合并这些值到自己的优先队列中,产生一个全局排序后的列表(注:这里只先返回_id标识和排序值用于排序,不返回整个数据,避免网络开销)。 - 协调节点根据排序后的列表获取相应的数据进行二次请求(注:这次是根据文档_id进行GET操作)doc。 - 获得数据进行返回。 优化前代码: ``` NativeSearchQueryHelper searchQueryHelper = QueryHelper.initES(EnumIndexType.WL_FXM_REPROMISE.getIndex() , EnumIndexType.WL_FXM_REPROMISE.getType()); searchQueryHelper.withFilter(QueryBuilders.termQuery("order_id",orderId)); searchQueryHelper.withPageable(new PageRequest(0,1)); searchQueryHelper.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("repromise_link").gt(0))); wlSecurityElasticsearchTemplate.query(searchQueryHelper.build(), new ResultsExtractor <OrderDto>(){} ``` **接口优化方案:** - 参考优化前代码,核心服务接口查询条件非常简单,通过订单号和repromise_link大于0去匹配查询结果。分析索引发现,查询条件对应的订单号正好是索引doc_id,于是调整ES查询策略,通过ES网关定向转发至对应的数据节点上(参考优化后代码),直接返回结果,根据返回结果,内存进一步过滤掉repromise_link为空或者小于0的数据。从而避免绝大部分ES数据节点的无效处理,提高查询性能。 - 重新规划ES资源,通过ES运维协助排查发现,当前ES集群数据节点规格不一致,存在一部分高性能服务器,一部分物理机老化服务器,将ES集群由原来24个规格不一的数据节点统一调整为16个统一规格性能服务器。 优化后接口性能提升比较明显,核心监控指标TP99从4871ms提升至19ms,AVG从963ms提升至2ms(注:优化前响应时间1S,主要是受限于ES集群大部分数据节点物理机老化,通过更换高性能数据节点机器和数据清洗后,核心接口TP99响应时间可以提高至200MS左右。大促期间,我们第一时间采用以上策略,优先保障上下游业务正常开展)。 **优化后代码:** ``` GetRequest getRequest = new GetRequest(); getRequest.id(orderId); getRequest.index(EnumIndexType.WL_FXM_REPROMISE.getIndex()); getRequest.type(EnumIndexType.WL_FXM_REPROMISE.getType()); ActionFuture<GetResponse> getResponse = wlSecurityElasticsearchTemplate.getClient().get(getRequest); GetResponse getFields = getResponse.get(20, TimeUnit.SECONDS); String source = getFields.getSourceAsString(); if(StringUtils.isEmpty(source)){return null;} OrderDto orderDto = JSON.parseObject(source,OrderDto.class); if(orderDto!=null && StringUtils.isEmpty(orderDto.getRepromiseLink())){return null;} ``` **敲黑板:**ES Get查询,网关将请求定向转发至对应的数据节点,直接返回查询结果。Query查询网关则会转发至所有的数据节点,通过多次查询获取结果,具体执行过程上文有详细说明。 #### 4.3 worker优化 当前任务采用分页查询获取每一次任务所有待处理数据。在分析数据遗漏处理的时候,首先,跟进各业务表的数据加工是否正确,在各业务表数据加工时增加TS标识,通过TS标识观察一定周期,并没有发现数据数据加工存在延迟或者异常的情况,排出问题是由于数据加工不准确造成的,由此怀疑ES深度分页可能造成的问题。接下来,依据查询条件跟踪一次任务待处理数据总量,发现每一次任务处理的异常数据量很大,大概在30万左右,那么一天处理的数据总量在千万级别,与每一天实际推送外部的20万异常数据总量差异非常巨大。进一步定位每一次任务执行数据范围,跟踪计算过程中,发现95%的数据都不满足当前在履约计算条件,由此发现任务查询条件粒度太粗,太多无用数据被加载出来。通过以上分析,制定优化方案如下,最终实践结果证明其优化方案是可行的。 **worker优化方案:** - ES普通分页查询改为Search_After查询,修正深度分页性能较慢问题,修正因为待处理数据量过大,ES普通查询无法命中需处理数据问题,导致部分单据遗漏处理。 - worker执行频率从5分钟调整为20分钟一次,降低执行频率,减轻ES压力。 - worker任务计算结果从单条更新调整为批量更新ES,保证ES运行稳定(注:ES单条更新与批量更新时间消耗几乎是一样的,批量操作可以减少大量网络开销)。 - 增加ES查询条件,精确定位待计算数据范围,降低待处理业务数据量,降低ES cpu使用率。 - 当前60个计算任务通过子任务形式挂载在三个主任务下,造成机器负载很高,甚至是挂掉。通过分析发现,任务之间不存在必然关联关系。通过调整优化JdTask 运行机制配置,拆分任务配置,每一个任务单独配置。 - 部署层面将任务划分至不同分组,避免任务之间相互影响。 **敲黑板:** 假设我们现在要从16分片数中,查询from = 5000 size = 500的数据集.意味着 ES需要在各个分片上匹配排序并得到5500条数据,协调节点拿到这些数据再进行排序等处理,然后结果集中取最后500条数据返回。我们会发现这样的深度分页将会使得效率非常低,因为我只需要查询500条数据,而ES则需要执行from+size条数据然后处理后返回。执行原理及过程参考上文Query查询执行顺序。 #### 4.4 redis优化 在整体优化过程中,积极响应集团资源优化号召,最大限度提升各中资源利用率和减少开销。结合实际业务,发现redis使用也存在可优化空间。 **redis优化方案:** - 缩减redis key长度,用缩写替代原有较长字符,减少redis 内存使用量。 - 使用支持压缩列表ziplist编码的hash数据结构替代键值对,降低键的数量与内存使用,进而节省内存空间。同一对象属性使用hset存储,避免使用多key-value或json存储,减少redis key数量,减少IO字节数,提高查询性能。 **敲黑板:**一个键值对key-value直接存储效率高于使用hset存储,使用hset存储是可以合并多个key,从而大量减少redis key数量,综合提升存储空间和写入性能,实现效率和空间的平衡。 ### 5 效果 通过数据清洗、代码优化、redis压缩等多种常规综合优化手段后,在支撑业务体量不变的前提下,系统整体资源缩减50%以上的前提下,达成了接口性能提升近500倍,业务并发量提升3.5倍,达到优化预期效果。 1)在支撑业务体量不变的情况下,redis资源从2T降至1T,缩减50%,ES资源CPU从704C降至236C,Memory从2816G降至944G,整体缩减66.5%,如下图。 ![](//img1.jcloudcs.com/developer.jdcloud.com/9a510713-ef5d-44a5-b43c-0d1d7910f97320220407141034.png) 2)支撑催单业务repromise时效查询核心服务接口性能较之前整体提升近500倍,核心监控指标TP99从4871ms提升至19ms,AVG从963ms提升至2ms,如下图。 ![](//img1.jcloudcs.com/developer.jdcloud.com/881dbe9b-aef3-43ec-bf36-0c27d8197a5620220407141051.png) 3)通过压测,系统核心服务接口并发量支撑从2万提升至7万; ![](//img1.jcloudcs.com/developer.jdcloud.com/474e3ab1-5ae7-44ff-9917-c4e0d39e341720220407141110.png) 4)JRC计算资源从960C缩减至488C,容器资源优化49%。 ![](//img1.jcloudcs.com/developer.jdcloud.com/932062fe-eb5c-405c-9847-fa5a3ec29e1f20220407141127.png) ### 6 总结 系统优化并非就一定要通过引进新技术、调整系统架构来实现。我们可以运用常规技术手段,结合实际业务情况,也能达到事半功倍的效果。 ------------ ###### 自猿其说Tech-JDL京东物流技术数据智能部 ###### 作者:猎鹰小分队 姚再毅 张晓起
原创文章,需联系作者,授权转载
上一篇:京东物流全链路压测技术解密
下一篇:一个宁静祥和没有bug的下午和SqlSession的故事
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号