DGraph是得物自主研发的新一代推荐系统核心引擎,基于C++语言构建,自2021年启动以来,经过持续迭代已全面支撑得物社区内容分发、电商交易等核心业务的推荐场景。DGraph在推荐链路中主要承担数据海选和粗排序功能,为上层精排提供高质量候选集。
核心技术特性:
系统依赖架构:
服务规模:
本文主要介绍DGraph系统在2024年的一些重要改进点。主要包括两次架构调整 + 性能优化 + 用户体验提升方面的一些工作。
在2023年前,DGraph系统始终采用单一集群架构提供服务。该架构模式在平台发展初期展现出良好的经济性和运维便利性,但随着业务规模扩张,单集群架构在系统层面逐渐显露出三重刚性约束:
系统改进点是在DGraph中增加了访问了其他DGraph集群 & FeatureStore特征集群的能力(图1)。为了成本考虑,我们复用了之前系统的传输协议flatbuffers,服务发现仍基于ZooKeeper。
改造的难点在图化集群!
目前推荐业务的核心场景都进行了图化改造,图化查询是把多路召回、打散、融合、粗排等策略打包到一个DAG图中一次发送到DGraph,DGraph的算子调度模块根据DAG的描述查询索引数据 & 执行算子最终把结果返回给业务系统,但这些DAG图规模都很大,部分业务DAG图涉及300+算子,因此如何在垂直拆分业务中把这些DAG图拆分到不同的DGraph集群中是一个非常复杂的问题,我们主要做了三方面改进:
垂直拆分集群,虽然把推荐N路召回分散到了M个集群,但是每个集群中每个表依然是全量。随着得物业务的发展,扩类目、扩商品,部分业务单表的数据量级已经接近单集群的存储瓶颈。因此需要DGraph中引入数据水平拆分的能力。
在DGraph分布式架构设计中,重点考虑了部署成本优化与业务迁移工作量:
在DGraph中,基于DGraph DAG图(参考图9)的一次查询就是图查询,内部简称graphSearch。在一个DAG图中,每个节点都是一个算子(简称Op),算子通过有向边连接其他算子,构成一个有向无环图,算子执行引擎按DAG描述的关系选择串行或者并发执行所有算子,通过组合不同算子DAG图能在推荐场景中灵活高效的完成各种复杂任务。
在实际应用场景中受DAG图规模 & 超时时间(需要控制在100ms内)限制,算子执行框架的效率非常重要。在最开始的版本中我们使用过Omp & 单队列线程池,集群在CPU负载低于30%时表现尚可,但在集群CPU负载超过30%后,rt99表现糟糕。在降本增效的背景下,我们重点对算子执行框架进行了优化,引入了更高效的线程池 & 减少了调度过程中锁的使用。优化后目前DGraph 在CPU压力超过60%依然可以提供稳定服务。
线程池优化:将原1:N 的线程池-队列架构调整为M:N 分组模式。具体实现为将N个工作线程划分为M个执行组(每组N/M线程),各组配备独立任务队列。任务提交采用轮询分发机制至对应组队列,通过资源分区有效降低线程调度时的锁竞争强度。
调度器优化:在DAG调度过程中存在两个典型多写场景
优化JavaSDK - DGraph数据传输过程:在DGraph部分场景,由于请求引擎返回的数据量很大,解码编码耗时占整个请求20%以上。分析已有的解码编码模块,引擎在编码阶段会把待传输数据编码到一个FlatBuffer中,然后通过rpc协议发送到业务侧的JavaSDK,sdk 解码FlatBuffer封装成List<map> 返回给业务代码,业务代码再把List<map> 转化成 List<业务Object>。过程中没有并发 & sdk侧多了一层冗余转换。
优化方案如下:
目前我们已经把DGraph DAG图查询的调试能力集成到DIP平台。其原理是:DGraph 的算子基类实现了执行结果输出,由于算子的中间结果数据量极大,当调试模块发现调试标志后会先把当前算子的中间结果写入日志中,数据按TraceID + DAGID+ NodeID 组织,最终这些数据被采集到SLS日志平台。
从DIP平台调试DAG图请求,首先通过DGraph JavaSDK的调试入口拿到DAG图请求json,填入DIP平台图请求调试入口,发起请求。索引平台会根据请求体自动关联DAG图并结合最终执行结果通过页面的方式展示。DIP平台拿到结果后,在DAG图中成功的算子节点标记为绿色,失败的节点标记为红色(图6)。点击任意节点可以跳转到日志平台查看该节点的中间结果输出。可用于分析DAG图执行过程中的各种细节,提升业务排查业务问题效率。
基于Chrome浏览器中的TimeLine构建,用于DGraph DAG图查询时算子性能分析优化工作。TimeLine功能集成在算子基类中,启动时会记录每个算子的启动时间、等待时间、完成时间、执行线程pid等信息,这些信息首先输出到日志,然后被SLS日志平台采集。用户可以使用查询时的TraceID在日志平台搜索相关的TimeLine信息。
当我们拿到请求的TimeLine信息后,通过浏览器加载可以通过图形化的方式分析DAG执行过程中耗时分布。图7是一个DAG 请求,它有9个算子节点,图8是它的一次请求的TimeLine。通过分析这些算子的耗时,可以帮助我们定位当前DAG图查询的瓶颈点在哪里,从而精准去解决性能方面的问题。
在DAG图召回中,业务的召回通常都带有一些固定模式,比如一个业务在一个DAG图召回中有N路召回,每一路召回都是:① 查找数据;② 关联可推池;③ 打散; 它们之间的区别可能仅仅是召回数据表名不同或者传递的参数不同。通常我们业务调整或者算法实验调整只需要增加或者减少部分召回,原有模式下这些操作需要去新增或者修改DAG图,加上算法实验很多,业务维护DAG图的成本会非常高。
DAG动态子图的引入就是为了解决这类问题,首先我们在DAG图中配置一个模板子图,它仅仅描述一个行为模式,代表会涉及几个算子,算子之间的关系如何,实际的参数以及召回路的数量则由业务方在发起请求时动态决定。子图的执行和主图的执行共用同一套调度框架,共享运行时资源以降低运行开销。
图9是一个DAG召回使用DAG子图后的变化,它有8路召回,一个Merge节点,这些召回分为两类,一类是基于KV表(ForwardSearch)触发的向量召回,另外一类是基于KVV表(IvtSearch)触发的向量召回。引入DAG子图后,在主图中节点数量由17个降为3个。
过去四年,DGraph聚焦于实现得物推荐引擎体系从0到1的突破,重点完成了核心系统架构搭建、算法策略支持及业务迭代空间拓展,取得多项基础性成果。基于2024年底的用户调研反馈结合DGraph当前的发展,后续将重点提升产品易用性、开发与运维效能及用户体验,同时在系统稳定性、可扩展架构和平台化建设方面持续深化。
文 / 寻风
关注得物技术,每周一、三更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。