小紅書離線數倉提效新思路,提升百倍回刷性能
數據處理效率一直是大數據時代的核心話題,它推動著各類數據執行引擎持續迭代產品。從早期的 MapReduce,到今天的 Spark,各行業正不斷演進其離線數倉技術架構。
現有以 Spark 為核心的數倉架構在處理大規模數據回刷方面已取得進展,但在資源和時間消耗上仍面臨挑戰。為了突破這些限制,小紅書數據倉庫團隊將 StarRocks 融入到離線處理流程,替換掉部分 Spark 處理的任務,并優化較為耗時的 Cube 計算,大幅度提高了數據的執行效率。
實踐證明,經過改造的離線處理鏈路,可以有效降低任務資源消耗,提前數據產出時間。將作業執行時間從小時級壓縮至分鐘級,計算資源使用量降低 90% 以上,日數據產出時間提前 1.5 小時,回刷時間減少 90%,回刷成本減少 99% 以上。
一、離線數倉技術架構
為了更好地管理和使用數據,離線數倉一般會通過分層設計,確保數據高效利用。
- ODS 層(操作數據存儲層):收集來自客戶端和服務端數據的原始日志。其中,服務端數據存儲結構與線上表結構保持一致。
- DWD 層(事實明細層):ODS 層數據在此層進行清洗和整合,經歷必要的數據轉換和計算,從而形成一個詳細的、一致的、歷史的和集成的數據集。
- DWS 層(數據聚合層):該層匯總 DWD 層數據,分為輕度匯總和匯總。輕度匯總維度較多,便于上卷,形成匯總層。數據一般為當天的計算或加總。
- DM 層(數據寬表層):這一層有確定的核心實體或者場景,可能跨數據域。根據業務需求,基于某個分析主題進行數據加工,對 DWS 層數據進一步地加工處理,形成各種豐富的數據模型。與 DWS 層的主要區別在于:度量值中是否包含“一天以外的加工數據”,如近 7 日,近 30 日,近 90 日等多日聚合指標。
- APP 層(數據應用層):在這里,DM 層的數據結果會被轉化為直觀的報表、動態的大屏、和便捷的數據服務,以支持決策和業務洞察。為了提升查詢效率,數倉會預先計算 Cube(即不同維度組合下的指標),將其存儲在表中。
- DIM 層(公共維度層):這一層用于存儲各類實體的維度數據,為數據分析提供多角度的視野。
離線數倉一般以 Spark 引擎作為主力,它負責數據的清洗、關聯和聚合,完成所有數據模型的建設。隨后,通過 DTS 任務將 APP 層的數據導入到 OLAP 集群中。小紅書主流的 OLAP 引擎包括 StarRocks 和 ClickHouse,它們憑借 OLAP 引擎的查詢能力,為數據產品、分析看板和業務工具提供數據查詢服務。
二、面臨的問題
雖然 Spark 引擎以其強大的吞吐量和穩定性在離線數倉中被廣泛使用,但它在數據查詢優化方面存在局限。Spark 并不直接管理數據的分布、存儲格式或元信息,無法結合數據存儲格式和數據元信息進行查詢優化。此外,為了確保穩定性,Spark 在跨節點數據傳輸時需要將數據寫入磁盤,這在大規模數據回刷時會導致資源消耗巨大和處理周期延長。
從本質上來看,Spark 僅僅是一個數據處理引擎,而不是一個理想的數據倉庫分析引擎。在實際應用中,這種性能瓶頸尤為明顯,開銷較大。例如,以交易運營行業為例,若要回刷兩年的數據,則需要占用相當于 7 萬臺機器近 30 天的資源,成本高達上百萬元。這種定期數據回刷產生的巨額成本,已經成為數據倉庫團隊不得不面臨的問題。
三、技術選型
與 Spark 這類數據處理引擎不同,基于 MPP 架構的 OLAP 引擎在數據查詢方面是更具優勢的。市面上常見的 OLAP 引擎主要有兩個:ClickHouse 和 StarRocks。
ClickHouse 是一個開源的列式數據庫管理系統,可用于 OLAP 分析。它采用列式存儲,與傳統的行式存儲相比,這種設計在處理分析型查詢時更為高效,因為它能夠快速讀取和聚合列數據,無需加載整個行。ClickHouse 的 MPP 架構允許查詢任務被拆分為多個子任務,并在集群的多個節點上并行執行。每個節點都配備獨立的的處理器和存儲資源,使得系統能夠充分利用集群的計算和存儲能力,大幅提升查詢速度和系統吞吐量。此外,ClickHouse 的 MPP 架構還支持數據復制和分片,提高數據的可用性和查詢性能。即使某個節點發生故障,其他節點也能迅速接管任務,確保服務的連續性。ClickHouse 是用 C++ 編寫的,它在單核性能上進行了深度優化。
StarRocks 也是一款高性能分析型數據倉庫,可實現多維、實時、高并發的數據分析。StarRocks 采用了向量化、MPP 架構、CBO 優化器、智能物化視圖和列式存儲引擎等先進技術,因此與同類產品相比,在查詢效率上具有較大優勢。StarRocks 能夠高效地從各類實時和離線數據源導入數據,并直接分析數據湖中的多種格式數據。StarRocks 兼容 MySQL 協議,常用 BI 工具能輕松接入。此外,StarRocks 支持水平擴展,確保了高可用性、可靠性和易于維護。
在小紅書內部,StarRocks 版本以存算一體架構為主,其中前端(FE)負責元數據管理和構建執行計劃,而后端(BE)則負責數據存儲和計算。這種架構使得查詢能夠直接在 BE 節點上本地執行,避免數據傳輸與拷貝開銷,從而實現極速的查詢分析性能。存算一體架構還支持數據的多副本存儲,提升了集群在高并發環境下的查詢能力和數據可靠性。
StarRocks 對算子和函數進行了向量化加速,并通過 Pipeline 調度框架,充分利用多核計算能力,提升查詢性能。雖然 StarRocks 和 ClickHouse 在單表查詢性能上相近,但 ClickHouse 在查詢并發和不支持分布式 Join 的局限性,使其不適合作為生產數倉模型的查詢加速引擎。因此,我們選擇了 StarRocks 替換原有的 Cube 計算,期望在數據處理和分析方面達到更高的性能和效率。
四、架構改造
為了提升離線數倉的產出效率,我們對架構進行如下優化:
- 直接導入:將 DM 表、DWS 表和常變維度的 DIM 表直接導入 StarRocks 中,簡化數據處理流程。
- Cube 表建模:在 StarRocks 中完成計算密集型的 Cube 表建模,以提高數據處理速度。
計算 UV 的一般方式是使用 count distinct ,它能夠保留原始數據的明細,有較高的靈活性。然而,由于在查詢執行的過程中需要進行多次 shuffle(跨節點通過網絡傳輸數據),會導致查詢性能隨著數據量增大而直線下降。
以下面的 SQL 為例,示例 1 :
select
seller_level,
count(distinct if(buy_num>0, user_id,null)) buy_uv,
count(distinct if(imp_num>0, user_id,null)) imp_uv,
count(distinct if(click_num>0, user_id,null)) click_uv
from
tb
group by seller_level
其執行過程中,首先會構建一個中間表 tb1,并擴展出三個虛擬維度:c1、c2 和 c3。
- c1: if(buy_num>0, user_id,null)
- c2: if(imp_num>0, user_id,null)
- c3: if(click_num>0, user_id,null)
因為有三個 count distinct 的維度,數據也會擴展為三倍。隨后經歷三輪 shuffle 才能得出結果。該過程中數據會膨脹,因此 shuffle 的數據量會比較大。
針對 Cube 表中的 id 消重指標,如用戶數、商品數等,我們采用了 BitMap 技術。BitMap 基本原理是用一個 bit 位來標記某個元素對應的 Value,而 Key 即是該元素。與傳統的 count distinct 方法相比,BitMap 消重在空間和時間上都顯示出顯著優勢:
- 空間優勢: BitMap 通過一個 bit 位標記 id 的存在,可看作是對一個集合的壓縮結構,大幅減少了存儲需求。比如對 int32 去重,使用普通BitMap 所需的存儲空間只占傳統去重的 1/32。StarRocks 采用的 Roaring Bitmap,能進一步降低稀疏數據的存儲空間。
- 時間優勢: BitMap 去重的計算操作,分為對給定下標的 bit 置位和統計 bitmap 的置位個數,時間復雜度分別為O(1)和O(n),且后者可使用 clz、ctz 等指令高效計算。此外, BitMap 去重技術在 MPP 執行引擎中還可以并行加速處理,每個計算節點獨立地生成其對應的子 BitMap,然后通過 bitor 操作高效地將這些子 bitmap 合并為一個完整的去重結果。與傳統的基于排序(sort)或哈希(hash)的去重方法相比,bitor 操作不僅減少了數據的無條件依賴和依賴關系,還能夠實現向量化處理,從而大幅提升去重操作的效率和性能。
BitMap 大小取決于最大 id 值,直接關系到查詢的穩定性和性能。StarRocks內置的編碼函數能夠將字符串類型的 id 轉換為 64 位的數字 id,但這樣的轉換可能導致生成的數字 id非常大,影響性能和穩定性。為了解決這個問題,我們引入了編碼表,它的作用是將字符串 id 映射到一個更小范圍的數字 id,隨后我們把數字 id 轉化為 BitMap。
編碼表的邏輯類似于數據庫的自增邏輯,即首個 id 對應的數字是 1,后續每新增一個 id,對應的數字 id 就自增 1。從而保證每個字符串 id 都會擁有一個唯一的數字 id,也有效縮小了 BitMap 占用的存儲。
那么經過 BitMap 改造的任務,示例 1 中的 SQL 執行過程就變成了下圖的執行過程。shuffle 數據量等于原表數據量,并且只需要一輪 shuffle。
使用過程中,Cube 計算會占用大量的 CPU 資源和內存資源。在我們的應用場景中,需要處理的 Cube 數量多達上百個,這對 StarRocks 來說,經常會導致內存溢出(OOM)的情況。StarRocks 在執行 SQL 查詢時,一般會將所有數據置于內存中,且計算過程中的數據不會 Spill 到磁盤上。為解決資源瓶頸這一問題,我們從兩個方面進行了優化:
- 控制 DM 表和 DWS 表的規模,這包括控制表的行數、列數、以及單字段大?。豢捎行p少數據表展用的資源。
- 優化 SQL 寫法。Cube 計算的核心原理是將數據擴展為 n 份(由 Cube 的數量決定),然后進行聚合操作。為了減少在擴展過程中產生的數據量,我們根據集群的規模和能力,將復雜的 SQL 查詢拆分成多個較小的批次。通過分批次提交這些查詢,巧妙地利用時間來換取所需的計算空間,從而避免了一次性處理大量數據導致的資源不足問題。
為了提升查詢效率,數據倉庫通常會在 APP 層創建多個 Cube,從一張寬表派生出多個針對不同業務場景的 Cube 表。這些 Cube 表雖然優化查詢效率,但并不承擔指標定義的功能。在不降低查詢效率的前提下,StarRocks 提供了物化視圖簡化數據模型。物化視圖本質是預先計算并存儲在 StarRocks 中的數據,它對用戶透明,在查詢時自動將請求重定向到已計算好的數據集,從而減少了數據處理量并加快了查詢速度。
例如,如下圖所示,未使用物化視圖的查詢(左側)需要從基礎底表中提取數據,而啟用物化視圖后(右側),查詢直接訪問優化后的數據,物化視圖的數據是底表數據關聯聚合而來,可以顯著減少數據量和提升查詢速度。
對于離線數據的物化視圖,一般為定時調度,其調度類似于天級離線任務,因此其調度不會對資源造成過多占用。
在數據產品中,用戶的查詢往往遵循一定規則、靈活度受制于產品,這為物化視圖提供了優化的機會。所有依賴同一張寬表的指標都可以通過物化視圖得到加速,而無需在多個表中重復定義。這樣,物化視圖在后臺靜默地提高了查詢效率。
此外,StarRocks 通過 Colocation Join 功能進一步加速表的連接操作。該功能將一組具有相同分布的表分片組織成一個集合,并確保這些 Table 的分桶副本位于同一組節點上。在執行分桶列上的 Join 操作時,可以在本地節點上直接完成,減少數據在節點間的傳輸耗時。
五、應用案例舉例
5.1 案例背景
業務運營團隊的組織架構調整導致行業類目不定期變更,多個數據產品如 OneDash(公司/業務經營看板)、鷹眼(電商運營平臺)、交易核心看板以及核心寬表都需要進行數據回刷。傳統的 Spark 任務回刷成本高昂,迫切需要優化。
5.2 鏈路改造
以交易核心看板和 OneDash 為例,原先的數據處理完全依賴于 Spark 引擎。出于性能考慮,商品行業的 Cube 表細分為兩個版本:一個包含行業新老客戶信息,另一個則不包含。然而,從業務需求出發,這兩個版本的 Cube 表實際上可以合并為一張。鑒于 Cube 表計算的執行時間占比最大,可以將這一計算過程遷移至 StarRocks 平臺,提升效率。
如上圖所示,改造后的新鏈路經過優化,最終對外只開放兩個 Cube 表:商品行業新老客 Cube 表、商家行業 Cube 表。
- 商品行業新老客 Cube 表整合了老鏈路中的兩個獨立表——商品行業新老客 Cube 和商品行業 Cube。新表直接依賴于一張綜合的商品行業用戶渠道寬表,該寬表包含了商品行業和新老客戶維度的關鍵信息以及多種指標。這一合并減少了維護的復雜性。
- 商家行業 Cube 表的鏈路也類似,它依賴于商家行業用戶渠道寬表,而這個寬表本身依賴于商品行業用戶渠道寬表產出。
這樣設計的原因:1)保證商品行業 Cube 指標和商家行業 Cube 指標的一致性;2)StarRocks 中的關聯操作可以使用 Colocate Join,效率比 Spark 要高。
分不同維度計算用戶數、商品數和商家數時,我們會先對 user_id、spu_id 和 seller_id 進行編碼,然后在中間表中構建對應的 BitMap。
5.3 回刷鏈路
面對行業變更,我們采取主備鏈路的策略來應對涉及多個數據產品的復雜回刷任務。主鏈路負責持續為線上產品提供實時服務,而備鏈路則專門用于執行數據的回刷操作。
- 在行業發生變更時,業務數據倉庫會根據最新的行業映射信息,重新構建備鏈路上的商品行業和商家行業維表。與此同時,主鏈路上的維表保持原行業映射不變,確保業務連續性?;厮⑦^去兩年的數據,包括商家行業維表、行業新老客維表,以及最新一天的商品行業維表。
- 歷史數據的回刷通過將商家行業和行業新老客維表的數據導入到 StarRocks 中來完成,而對于商品行業維表,只需回刷最新一天的數據。
- 接著我們更新商品行業維表下游的維表依賴關系,使其指向最新日期的數據,并調度起各業務的 Cube 回刷鏈路,對近 2 年的數據進行全面更新。這一整個過程都是通過 StarRocks SQL 任務來實現的。數據調度平臺則負責執行回刷計劃,關鍵表會部署數據質量檢測任務(DQC),保證回刷過程中的數據符合預期。
- 一旦所有的 Cube 回刷任務完成,我們便可以調度同步任務,利用 StarRocks 的外表導入功能,將備集群的更新結果同步到主集群中。這樣的同步操作確保了主鏈路數據的及時更新,同時也保障了數據的完整性和業務的連續運行。
5.4 收益
通過將回刷鏈路部署到 StarRocks 集群中,我們實現了資源的高效利用,無需申請其他額外資源。同時,主鏈路的運行依托于現有的線上集群,沒有額外消耗。這次鏈路改造帶來的主要收益可以分為兩大類:
- 回刷收益:以最近一輪的回刷為例,回刷 2022 年和 2023 年共計兩年的數據。我們對比了基于 Spark 和基于 StarRocks 的鏈路性能。結果顯示,StarRocks 鏈路在資源消耗和成本上都有顯著的減少,回刷時間節省 90%,回刷成本降低 99%。具體來說,資源消耗從上千萬 GBHour 降低到 幾十萬 GBHour,成本從上百萬元大幅下降到幾千元,回刷時間從一個月縮短到幾天。
- 日常收益:在日常數據處理方面,StarRocks 鏈路同樣展現出色。與 Spark 鏈路相比,StarRocks 沒有額外資源消耗,每天的數據產出時間提前了 1.5 小時以上,數據處理時間縮短至幾分鐘,這樣的改進不僅加快數據處理速度,還提高整體的工作效率。
六、總結與展望
OLAP 引擎在實時數倉建設方面已經得到了廣泛的應用。我們的實踐證明,結合業務特點,在處理中小規模數據量時,使用 StarRocks 等分布式 OLAP 引擎替換 Spark ,承擔更多的離線處理任務,可以顯著提高數據倉庫產出的速度和效率,達到降本增效的目的。
展望未來,我們計劃進一步探索 StarRocks 在湖倉一體和存算分離的應用場景,以構建更加高效、靈活的數據生產鏈路和自助分析產品。我們期待通過這些創新實踐,能夠為公司帶來更強大的數據處理能力,支持業務的持續增長和決策的精準性。
七、作者簡介
- 黃猿(吳筱琦)
小紅書數據倉庫工程師,現負責渠道歸因和數據任務性能優化。
- 馬爾科(吳浩亮)
小紅書數據解決方案專家,現負責小紅書用戶增長、搜推、基礎流量、電商、直播等多個業務領域數倉建設。
- 凌波(李娟)
小紅書交易數據倉庫開發,現負責小紅書交易 C 端的數據建設