得物自研DGraph4.0推薦核心引擎升級之路
一、前言
二、架構升級
1. 垂直拆分業務集群支持
2. 分布式能力支持
三、性能優化
1. 算子執行框架優化
2. 傳輸協議編碼解碼優化
四、用戶體驗優化
1. DAG圖調試功能優化
2. DAG圖支持TimeLine分析
3. DAG圖支持動態子圖
五、展望未來
一、前言
DGraph是得物自主研發的新一代推薦系統核心引擎,基于C++語言構建,自2021年啟動以來,經過持續迭代已全面支撐得物社區內容分發、電商交易等核心業務的推薦場景。DGraph在推薦鏈路中主要承擔數據海選和粗排序功能,為上層精排提供高質量候選集。
核心技術特性:
- 索引層 - 支持KV(鍵值)、KVV(鍵-多值)、INVERT(倒排)、DENSE-KV(稠密鍵值)等。索引存儲支持磁盤 & 內存兩種模式,在預發等延遲壓力低場景,通過磁盤索引使用低規格服務器提供基本服務。線上場景使用內存索引保證服務穩定性,提供毫秒級延遲響應。索引更新支持雙buff熱更新【內存足夠】、服務下線滾動更新【內存受限】、Kafka流式數據實時更新等三種模式。
- 查詢層 - 支持向量檢索IVF & HNSW、鍵值(KV)查詢、倒排檢索、X2I關聯查詢、圖查詢。對外提供JavaSDK & C++ SDK。
系統依賴架構:
- 索引全生命周期管理由得物索引平臺DIP統一管控。
- 服務發現基于ZooKeeper(zk)。
- 集群資源調度基于得物容器平臺,目前已經支持HPA。
服務規模:
- 目前在線100+集群,2024年雙11在線突破了100W QPS。
本文主要介紹DGraph系統在2024年的一些重要改進點。主要包括兩次架構調整 + 性能優化 + 用戶體驗提升方面的一些工作。
二、架構升級
垂直拆分業務集群支持
在2023年前,DGraph系統始終采用單一集群架構提供服務。該架構模式在平臺發展初期展現出良好的經濟性和運維便利性,但隨著業務規模擴張,單集群架構在系統層面逐漸顯露出三重剛性約束:
- 存儲容量瓶頸 - 單節點內存上限導致數據規模受限;
- 網絡帶寬瓶頸 - 單物理機Pod共享10Gbps帶寬,實際可用帶寬持續承壓,推薦引擎業務中部分核心集群200余張數據表(單表需20分鐘級更新)的實時處理需求已遭遇傳輸瓶頸;
- 計算能力瓶頸 - 單實例最大64核的算力天花板,難以支撐復雜策略的快速迭代,核心場景響應時效與算法復雜度形成顯著沖突;
- 穩定性 - 大規格集群對于容器調度平臺不友好,在擴容、集群故障、集群發布時耗時較久;基于得物平臺推薦數據量增長和算法迭代需求,我們實施業務垂直拆分的多集群架構升級,通過資源解耦與負載分離,有效突破了單節點資源約束,為復雜算法策略的部署預留出充足的技術演進空間。
系統改進點是在DGraph中增加了訪問了其他DGraph集群 & FeatureStore特征集群的能力(圖1)。為了成本考慮,我們復用了之前系統的傳輸協議flatbuffers,服務發現仍基于ZooKeeper。
圖 1 DGraph 訪問架構改進
改造的難點在圖化集群!
目前推薦業務的核心場景都進行了圖化改造,圖化查詢是把多路召回、打散、融合、粗排等策略打包到一個DAG圖中一次發送到DGraph,DGraph的算子調度模塊根據DAG的描述查詢索引數據 & 執行算子最終把結果返回給業務系統,但這些DAG圖規模都很大,部分業務DAG圖涉及300+算子,因此如何在垂直拆分業務中把這些DAG圖拆分到不同的DGraph集群中是一個非常復雜的問題,我們主要做了三方面改進:
- DAG管理 - 集群分主集群和從集群【多個】,DAG圖部署在存在主集群中,DIP平臺會分析DAG的拓步結構并把屬于從集群的部分復制出來分發給從集群,為了保證DAG的一致性,只允許從主集群修改DAG圖;
- 集群劃分 - 通常按召回劃分,比如Embedding召回、X2I召回、實驗召回可以分別部署在不同的集群,另外也可以把粗排等算力需求大的部分單獨放在一個集群,具體根據業務場景調整;
- 性能優化 - 核心表多個集群存放,減少主集群和從集群間數據交換量。
圖 2 DGraph業務垂直拆分集群
分布式能力支持
垂直拆分集群,雖然把推薦N路召回分散到了M個集群,但是每個集群中每個表依然是全量。隨著得物業務的發展,擴類目、擴商品,部分業務單表的數據量級已經接近單集群的存儲瓶頸。因此需要DGraph中引入數據水平拆分的能力。
圖 3 DGraph 分布式集群架構圖
在DGraph分布式架構設計中,重點考慮了部署成本優化與業務遷移工作量:
- 分布式集群采用【分片數2】×【雙活節點2】×【數據副本數2】的最小拓撲結構,理論上需要8臺物理節點保障滾動更新與異常容災時的穩定性。針對CPU負載較輕的場景,為避免獨立Proxy集群帶來的額外資源開銷,DGraph將Proxy模塊和DGraph引擎以對稱架構部署到所有節點,通過本地優先的智能路由策略(本地節點輪詢優先于跨節點訪問)實現資源利用率與訪問效率的平衡;
- 在業務兼容性方面,基礎查詢接口(KV檢索、倒排索引、X2I關聯查詢)保持完全兼容以降低遷移成本,而DAG圖查詢需業務側在查詢鏈路中明確指定Proxy聚合算子的位置以發揮分布式性能優勢。數據鏈路層面,通過DIP平臺實現索引無縫適配,支持DataWorks原有任務無需改造即可對接分布式集群,同時增量處理模塊內置分片過濾機制,可直接復用現有Flink實時計算集群進行數據同步。
三、性能優化
算子執行框架優化
在DGraph中,基于DGraph DAG圖(參考圖9)的一次查詢就是圖查詢,內部簡稱graphSearch。在一個DAG圖中,每個節點都是一個算子(簡稱Op),算子通過有向邊連接其他算子,構成一個有向無環圖,算子執行引擎按DAG描述的關系選擇串行或者并發執行所有算子,通過組合不同算子DAG圖能在推薦場景中靈活高效的完成各種復雜任務。
在實際應用場景中受DAG圖規模 & 超時時間(需要控制在100ms內)限制,算子執行框架的效率非常重要。在最開始的版本中我們使用過Omp & 單隊列線程池,集群在CPU負載低于30%時表現尚可,但在集群CPU負載超過30%后,rt99表現糟糕。在降本增效的背景下,我們重點對算子執行框架進行了優化,引入了更高效的線程池 & 減少了調度過程中鎖的使用。優化后目前DGraph 在CPU壓力超過60%依然可以提供穩定服務。
圖4 DGraph算子執行框架優化
線程池優化:將原1:N 的線程池-隊列架構調整為M:N 分組模式。具體實現為將N個工作線程劃分為M個執行組(每組N/M線程),各組配備獨立任務隊列。任務提交采用輪詢分發機制至對應組隊列,通過資源分區有效降低線程調度時的鎖競爭強度。
調度器優化:在DAG調度過程中存在兩個典型多寫場景
- 前驅算子節點完成時需并行更新后繼節點標記;
- DAG全局任務計數器歸零判斷。原方案通過全局鎖(Graph鎖+Node鎖)保障原子性,但在高負載場景引發顯著鎖競爭開銷,影響線程執行效率。經分析發現這兩個狀態變更操作符合特定并發模式:所有寫操作均為單調增減操作,因此可將鎖機制替換為原子變量操作。針對狀態標記和任務計數場景,分別采用原子變量的FetchAdd和FetchSub指令即可實現無鎖化同步,無需引入CAS機制即滿足線程安全要求。
傳輸協議編碼解碼優化
優化JavaSDK - DGraph數據傳輸過程:在DGraph部分場景,由于請求引擎返回的數據量很大,解碼編碼耗時占整個請求20%以上。分析已有的解碼編碼模塊,引擎在編碼階段會把待傳輸數據編碼到一個FlatBuffer中,然后通過RPC協議發送到業務側的JavaSDK,SDK解碼FlatBuffer封裝成List<map> 返回給業務代碼,業務代碼再把List<map> 轉化成 List<業務Object>。過程中沒有并發 & SDK側多了一層冗余轉換。
優化方案如下:
- 串行編碼調整為根據文檔數量動態調整編碼塊數量。各子編碼塊可以并發編碼解碼,加快編碼&解碼速度,提升整體傳輸性能;
- SDK側由 Doc -> Map -> JavaObject 的轉化方式調整為 Doc -> JavaObject,減少解碼端算力開銷。
圖5 DGraph 傳輸編碼解碼過程優化
四、用戶體驗優化
DAG圖調試功能優化
目前我們已經把DGraph DAG圖查詢的調試能力集成到DIP平臺。其原理是:DGraph 的算子基類實現了執行結果輸出,由于算子的中間結果數據量極大,當調試模塊發現調試標志后會先把當前算子的中間結果寫入日志中,數據按TraceID + DAGID+ NodeID 組織,最終這些數據被采集到SLS日志平臺。
圖6 DGraph DAG圖查詢調試
從DIP平臺調試DAG圖請求,首先通過DGraph JavaSDK的調試入口拿到DAG圖請求json,填入DIP平臺圖請求調試入口,發起請求。索引平臺會根據請求體自動關聯DAG圖并結合最終執行結果通過頁面的方式展示。DIP平臺拿到結果后,在DAG圖中成功的算子節點標記為綠色,失敗的節點標記為紅色(圖6)。點擊任意節點可以跳轉到日志平臺查看該節點的中間結果輸出。可用于分析DAG圖執行過程中的各種細節,提升業務排查業務問題效率。
DAG圖支持TimeLine分析
基于Chrome瀏覽器中的TimeLine構建,用于DGraph DAG圖查詢時算子性能分析優化工作。TimeLine功能集成在算子基類中,啟動時會記錄每個算子的啟動時間、等待時間、完成時間、執行線程pid等信息,這些信息首先輸出到日志,然后被SLS日志平臺采集。用戶可以使用查詢時的TraceID在日志平臺搜索相關的TimeLine信息。
圖7 DGraph DAG圖例子
圖8 使用瀏覽器查看DGraph DAG圖 TimeLine
當我們拿到請求的TimeLine信息后,通過瀏覽器加載可以通過圖形化的方式分析DAG執行過程中耗時分布。圖7是一個DAG 請求,它有9個算子節點,圖8是它的一次請求的TimeLine。通過分析這些算子的耗時,可以幫助我們定位當前DAG圖查詢的瓶頸點在哪里,從而精準去解決性能方面的問題。
DAG圖支持動態子圖
在DAG圖召回中,業務的召回通常都帶有一些固定模式,比如一個業務在一個DAG圖召回中有N路召回,每一路召回都是:① 查找數據;② 關聯可推池;③ 打散; 它們之間的區別可能僅僅是召回數據表名不同或者傳遞的參數不同。通常我們業務調整或者算法實驗調整只需要增加或者減少部分召回,原有模式下這些操作需要去新增或者修改DAG圖,加上算法實驗很多,業務維護DAG圖的成本會非常高。
DAG動態子圖的引入就是為了解決這類問題,首先我們在DAG圖中配置一個模板子圖,它僅僅描述一個行為模式,代表會涉及幾個算子,算子之間的關系如何,實際的參數以及召回路的數量則由業務方在發起請求時動態決定。子圖的執行和主圖的執行共用同一套調度框架,共享運行時資源以降低運行開銷。
圖9 DGraph 子圖
圖9是一個DAG召回使用DAG子圖后的變化,它有8路召回,一個Merge節點,這些召回分為兩類,一類是基于KV表(ForwardSearch)觸發的向量召回,另外一類是基于KVV表(IvtSearch)觸發的向量召回。引入DAG子圖后,在主圖中節點數量由17個降為3個。
五、展望未來
過去四年,DGraph聚焦于實現得物推薦引擎體系從0到1的突破,重點完成了核心系統架構搭建、算法策略支持及業務迭代空間拓展,取得多項基礎性成果。基于2024年底的用戶調研反饋結合DGraph當前的發展,后續將重點提升產品易用性、開發與運維效能及用戶體驗,同時在系統穩定性、可擴展架構和平臺化建設方面持續深化。