一、背景介紹
京東廣告圍繞Apache Doris建設廣告數據存儲服務,為廣告主提供實時廣告效果報表和多維數據分析服務。歷經多年發展,積累了海量的廣告數據,目前系統總數據容量接近1PB,數據行數達到18萬億行+,日查詢請求量8,000萬次+,日最高QPS2700+。 隨著業務的不斷增長與迭代,數據量持續激增,存儲資源逐漸成為瓶頸。近兩年存儲資源經歷了多次擴容,存儲容量增加了近十倍,而日查詢請求量僅增長兩倍。同時,計算資源的利用率因頻繁擴容而相應降低,導致資源浪費。通過對查詢請求的分析,我們發現日常查詢中有99%集中在近一年的數據上,數據使用呈現出明顯的冷熱現象。基于此,希望借助Apache Doris探索一種滿足線上服務要求的冷熱數據分層解決方案,在數據不斷膨脹的情況下,降低數據的存儲和使用成本。
二、冷熱分層方案介紹
截至當前,我們的數據冷熱分層實踐已歷經兩種方案,分別是Doris冷數據入湖和Doris冷熱數據分層。Doris冷數據入湖方案通過SDC(Spark-Doris-Connector)將Doris中的冷數據轉入湖中,入湖后的冷數據可通過Doris外表進行查詢。Doris冷熱數據分層方案則通過在Doris中設置數據的TTL時間,由Doris根據數據的TTL時間自動判斷冷熱數據,并將冷數據移至相對廉價的存儲介質。冷數據入湖方案借鑒了騰訊游戲的相關經驗(https://cloud.tencent.com/developer/article/2251030),并在Apache Doris 1.2版本中進行了實踐;而Doris冷熱數據分層方案則是最近上線的新一代冷熱數據分層方案。以下將結合我們過往的實踐經驗,簡要介紹這兩種方案。
冷熱分層V1:數據湖方案
在數據湖方案中,需將Doris的數據依據業務時間進行冷熱劃分。在類似場景中,業務時間即為Doris表的分區時間。為實現Doris數據入湖,需借助Spark-Doris-Connector(SDC)將Doris中的冷數據遷移至數據湖(如Iceberg)。查詢時,需根據業務時間對查詢進行冷熱區分,將冷數據查詢(冷查詢)與熱數據查詢(熱查詢)分別路由至不同的查詢引擎。冷數據查詢通過查詢改寫,將查詢重定向至數據湖對應的外部表;熱數據查詢則無需改寫,直接查詢Doris中的OLAP表。
數據入湖方案的優勢在于,冷數據的查詢與下載能夠與線上Doris系統實現解耦,從而確保線上操作的穩定性不受影響。這種解耦設計確保了冷數據的處理和查詢不會干擾線上Doris系統的正常運行。通過將冷數據與線上系統分離,可以確保線上系統在處理實時數據時保持高效和穩定。這對于需要高可用性和低延遲的在線廣告報表業務而言尤為重要。
數據入湖方案的主要劣勢包括以下幾點:首先,需借助ETL工具實現數據從Doris到數據湖的遷移。ETL工具能夠自動化數據遷移過程,但這也意味著需要額外的資源和時間來配置及運行這些工具。其次,為了獲取完整的數據集,必須對Doris中的熱數據和數據湖中的冷數據進行UNION操作。這意味著在進行數據分析或查詢時,需要同時訪問兩個不同的數據存儲系統,這不僅增加了系統的復雜性,還可能影響查詢性能。例如,如果一個分析需要同時查詢熱數據和冷數據,那么查詢時間可能會顯著增加,因為系統需要從兩個不同的地方獲取數據。最后,數據入湖后,Schema變更操作需得到相應數據湖的支持。這意味著如果需要對數據結構進行修改,例如添加或刪除字段,必須確保數據湖能夠支持這些變更。這可能需要額外的技術支持和維護工作。
冷熱分層V2:Apache Doris冷熱數據分層方案
Apache Doris 1.2 的冷熱數據分層方案基于本地磁盤,熱數據存儲于 SSD,而冷數據則轉移至性能較低的 HDD,以此實現數據分層。然而,此方案存在以下缺點:首先,該方案更適合物理機部署,而不適用于容器或 Kubernetes (K8S) 部署。當前,大多數公司已轉向基于容器或 K8S 的部署方式,物理機部署已較為罕見。其次,需要預先估算冷數據的存儲空間,然而,冷數據量會隨時間逐漸增加,難以準確預估其容量。因此,我們并未在 Doris 1.2 中采用 Doris 原生的冷熱數據分層方案,而是希望探索一種基于分布式文件系統作為冷數據存儲的新方案。
Apache Doris 2.0 的冷熱數據分層功能支持將冷數據存儲于如 OSS 和 HDFS 等分布式存儲系統中。用戶可通過配置相應的存儲策略來指定數據的冷熱分層規則,進而通過為表或分區設定存儲策略,實現冷數據自動遷移至外部存儲系統。基于分布式存儲的 Doris 冷熱數據分層方案具有簡潔性,避免了數據湖方案的復雜性。然而,該方案的缺點在于冷熱數據統一在一個集群中進行管理和使用,高優先級的熱查詢可能會受到冷查詢的影響,因此需要對冷數據查詢進行適當的限流。以下將介紹我們在從 Doris 1.2 的數據湖方案升級至 Doris 2.0 基于分布式存儲的冷熱數據分層過程中遇到的一些問題及其解決方案。
三、問題解決
3.1 Apache Doris2.0性能優化&問題修復
為了實現基于分布式存儲的冷熱數據分層,需將Doris集群由1.2版本升級至2.0版本。盡管我們在前期已與社區共同完成了大量Doris開發工作,但在具體實施冷熱數據分層過程中,仍遇到了若干問題。以下是幾個典型問題。
查詢性能下降問題
在性能Diff階段,我們發現,在報表小查詢場景(平均tp99<20ms)中,Doris 2.0相較于我們之前的Doris 1.2版本,性能下降約50%左右。經過分析,我們發現Doris 2.0的FE默認啟用了新的優化器,而該優化器在SQL Rewrite階段使用了更多的規則進行重寫,從而導致了性能下降。通過進一步的壓測、分析以及與社區的交流,我們得出結論:除非在Doris 2.0中對新優化器進行更深層次的優化,否則很難使性能達到Doris 1.2的水平。因此,在我們的應用場景中,我們關閉了Doris 2.0的新優化器功能。在使用舊優化器的情況下,我們還是遇到了以下性能問題:
分桶裁剪失效
當查詢命中表的Rollup后,底層數據掃描量明顯增多,查詢耗時較1.2明顯升高。查看執行計劃,發現執行計劃掃描了對應分區下面的所有分桶數據,分桶裁剪沒有生效。修復PR:[Fix](MV) query not hit partition in original planner by GoGoWen · Pull Request #38565 · apache/doris · GitHub
前綴索引失效
當從1.2升級到2.0時,升級前于1.2時創建的Date類型的字段在查詢時如果將它和DateTime類型(如類似Date>="2024-10-01 00:00:00")進行比較,FE會對Date類型進行自動類型提升(類似CAST(Date as datetime) >= Datetime("2024-10-01 00:00:00"))。 提升后的謂詞在BE處理的時候和底層數據存儲的實際類型(Date("2024"))不能進行比較,導致對應前綴索引失效,引起查詢性能大幅下降。這種情況我們通過在FE端進行類型對齊進行了修復https://github.com/apache/doris/pull/39446。 修復后索引生效,性能得到大幅提升。
FE CPU使用率高問題
在對Doris2.0進行壓力測試時,觀察到FE節點的CPU使用率相較于Doris1.2顯著上升,在相同的QPS請求下,Doris2.0的CPU使用率幾乎翻倍。資源消耗明顯增加。在測試過程中,我們對FE節點進行了火焰圖分析,識別出性能消耗較高的函數;同時,我們與社區成員進行了充分的溝通,最終確定了多個資源消耗點,并實施了相應的優化措施。
時間比較效率優化
廣告報表場景下時間比較操作是幾乎每個查詢在分區裁剪時都會用到,而Doris2.0對時間的比較需要先轉化為字符串再進行比較,這種比較沒有直接使用數據結構自身的成員變量進行比較效率高,這里我們通過PR:https://github.com/apache/doris/pull/31970對分區裁剪時的時間比較操作進行了優化,優化后CPU使用率整體降低25%左右。
物化視圖字段列重寫優化
在表有Rollup而沒有物化視圖時,Doris FE對查詢的執行計劃還是會使用只需作用于物化視圖的改寫規則進行優化改寫,這些無效的改寫不僅造成CPU利用率提升還會影響查詢延時。 PR: https://github.com/apache/doris/pull/40000對這種情況進行優化,在無物化視圖情況下避免無意義的執行計劃改寫。
此外,我們還通過使用for循環代替流操作、關鍵路徑減少日志輸出等進行了CPU使用率優化,最總Doris 2.0 FE CPU消耗最終達到1.2版本等同水平。
BE 內存使用率高
在對 Doris 2.0 版本的集群使用過程中,發現BE內存使用率會極緩慢持續升高,長期使用的情況下,Doris BE 階段存在 OOM 風險。排查該現象和 SegmentCache的配置有關:
Doris2.0使用了SegmentCache,用于對底層數據文件對象緩存。但2.0對于SegmentCache的內存使用計算存在問題且默認閾值設置過大,導致一直觸發不到SegmentCache使用閾值;隨著segment文件數量的增加,SegmentCache使用量會越來越大。結合日常內存使用量的評估及壓測驗證,我們重新調整了合理的SegmentCache使用閾值;在保證Cache命中率基本不變的情況下降BE常駐內存使用率從 60%以上降低到25%一下,有效避免了 BE 節點 OOM的風險。
經過一系列優化后,2.0版本查詢性能參數(TP99耗時、FECPU消耗、BE CPU消耗)有了較大優化,基本和1.2版本對齊。
3.2 冷數據 Schema Change(SC)優化
Schema Change(SC) 是Apache Doris等實時數倉日常使用當中的高頻操作,其中,Add Key Column 的操作是廣告數據報表中使用較多的場景,實踐中發現冷數據添加 Key 列的SC操作存在如下問題:
1.Schema Change 退化:冷數據的Add Key Column 操作會退化成Direct Schema Change(DSC);DSC操作比較重,需要對全量數據進行重新讀取和寫入。在實際使用過程中對于含大量冷數據的表進行 Add Key Column 操作需要重新對遠端海量數據進行讀寫,增大系統IO負載的同時,SC 任務耗時也很長。實踐中一張冷數據量20T左右的表,整個SC耗時在7天以上,對于需要緊急上線的業務體感極差。
2.數據冗余:冷數據Schema Change(SC)時,Tablet 的每個副本都會獨立進行 SC 操作,導致原來冷數據單副本存儲在SC后變成多副本,冷數據存儲資源浪費嚴重。
為了優化和修復上述冷數據 Schema Change 遇到的問題,我們對冷數據的 Schema Change 進行了如下優化:
實現冷數據 Linked Schema Change
針對冷數據 Add Key Column類型的SC 退化成 Direct Schema Change 導致 SC 任務執行緩慢的問題,我們對冷數據Add Key Column類型SC的流程進行深度優化,利用遠端存儲系統(ChubaoFS)的CopyObject接口,實現在遠端直接進行數據復制,避免數據文件從遠端拉取到Apache Doris,經數據重寫后再存儲到遠端存儲系統的巨大IO開銷。該優化減少了兩次網絡傳輸和一次數據轉換的開銷,這樣能夠極大加速Add Key Column場景下冷數據Schema Change的執行速度。經測算優化后SC執行速度提升40倍, 相關PR已合并社區2.0分支:[branch-2.0](schema change) opt cooldown data schema change by DarvenDuan · Pull Request #40963 · apache/doris · GitHub。
實現冷數據單副本SC
Apache Doris 2.0中冷數據在進行Schema Change時,同一份數據的多個副本之間相互獨立進行(SC)。如此, Schema Change完成后造成冷數據將在遠端存儲存在多份,造成存儲資源浪費的同時也降低SC任務執行效率。為了解決這個問題,參考Raft協議對冷數據多副本SC場景進行了優化。即SC只在選舉出來的Leader副本上執行,非Leader副本只生成元數據。
我們已成功解決了SC后數據副本冗余的問題。然而,仍存在一個潛在風險:FE會定期檢查BE上的tablet SC操作是否正常。我們允許不超過半數的tablet副本SC失敗,即使BE上的Leader副本SC失敗,整個SC任務仍可能成功。因此,若Leader副本在復制數據時失敗,可能會導致數據丟失。為避免這種情況,我們在FE對Schema Change任務的健康度判斷時,特別考慮了冷數據的“Linked Schema Change”。只有當tablet的Leader副本成功時,SC才會被視為成功。這樣可以確保數據的完整性和一致性。
實現冷數據的Light Schema Change
對于存量表中可以直接使用Light Schema Change的表,我們希望更進一步支持一種冷數據Light Schema Change;如果走Light Schema Change,則只需要修改FE 元數據信息,不需要進行BE端任務創建及數據文件處理;處理時間會達到毫秒級。
當前Light Schema Change只支持Value列字段的添加,不支持Key列字段添加。但對于不涉及分區、分桶、前綴索引的普通Key列;可以按照Light Schema Change的邏輯進行處理。這里對這一功能進行了升級。主要改動在FE階段Light Schema Change判斷階段,支持對Key列添加的邏輯。以滿足較普通的添加Key列操作。
3.3 其他問題解決
隨著整體數據量持續增長,在引入冷熱數據分層方案之前,為緩解線上存儲資源緊缺的現狀,我們將 Doris 歷史數據通過 backup 的方式結轉到外部存儲,維持 Doris 集群安全的存儲水位。完成Doris2.0版本升級后,再將結轉的歷史數據重新恢復至 Doris 集群。為了便捷高效地操作歷史數據,我們實現了一套統一的結轉和恢復工具,工具解決了如下三個問題:
1.歷史數據總量大,底表數量多,如何準確高效地結轉這些數據?
2.歷史數據持續結轉,線上表schema持續變更,如何將這部分 schema 不一致的數據重新恢復?
3.如何實現統一的冷熱數據分層和熱數據自動冷卻?
為解決第一個數據結轉的問題,我們實現了一個歷史數據自動結轉工具data migrator。支持將線上集群所有DB任意時間段內的數據異步并行結轉至外部離線存儲。
Doris2.0完成升級后啟動建設冷熱數據分層,首先需要將結轉至外部存儲的歷史數據恢復至線上環境。此時遇到的最大問題是線上表結構已發生多次變更,導致多次結轉的歷史數據備份snapshot文件所對應的schema結構與線上表不一致。為了解決schema不一致的問題,我們設計開發了一套自動化數據恢復工具narwal_cli,如下圖中Data Restore Process過程所示,narwal_cli工具支持自動對齊歷史結轉數據和Doris 集群中數據的的schema,并定向恢復至線上環境。
在實施恢復過程中,還遇到Flink2Doris實時寫入任務失敗的情況,具體信息如下:LOAD_RUN_FAIL; msg:errCode = 2, detailMessage = Table xxxxx is in restore process. Can not load into it
經排查,問題原因是Doris表在restore過程中伴隨實時數據寫入,寫入會對表當前的meta info進行check,但狀態檢測粒度較粗,僅檢測tableState而未進一步檢測partitionState,造成狀態誤判,進而影響了寫入任務。問題定位后迅速完成修復和發版上線,詳細信息可參考pr:[enhancement](Load)allow load data to the other partitions when some partitions are restoring by Johnnyssc · Pull Request #39595 · apache/doris · GitHub。以上問題解決后,在線上環境快速準確地恢復了所有歷史數據,且工具兼顧易用性,做到隨時啟停、斷點續傳。
在我們的應用場景中,我們對歷史恢復的數據和線上的數據分別設置了不同的冷熱分層策略:我們將歷史數據的storage_policy設置為cooldown_ttl=10s,實現歷史數據立刻冷卻至ChubaoFS。對全量熱數據則統一設置了cooldown_ttl=2years,實現線上熱數據隨著兩年時間窗口推進自動冷卻。整個歷史數據的恢復和冷卻全過程,做到對線上業務透明,準確高效地實現全量歷史數據恢復和冷卻。同時,在冷卻數據過程中發現冷熱數據策略設置異常問題,進行了修復,參考pr:https://github.com/apache/doris/pull/35270。實現統一的冷熱分層和自動冷卻后,后續存儲數據量繼續保持增長,也無需再擴容線上存儲資源,僅需擴容較低成本的外部離線存儲即可,實現計算資源利用率提升的同時,存儲經濟成本大幅降低。
除了以上優化,我們還在為 Apache Doris 在讀寫性能提升、問題修復、功能完善等方面積極貢獻,已為社區 2.0 版本提交并合并 30+ PR。
四、小結
通過對數據進行冷熱分層,我們的存儲成本降低了約87%。對比Doris 1.2的冷數據入湖方案與Doris 2.0的冷數據分層方案,后者在并發查詢能力上提升了超過10倍,查詢延遲顯著減少。此外,冷熱數據分層方案簡化了存儲和查詢的維護工作,降低了整體復雜性和成本。冷熱分層架構的成功實施,離不開Apache Doris社區和中臺OLAP團隊的鼎力支持,特此向所有Apache Doris社區和中臺OLAP團隊的成員表示衷心的感謝。展望未來,我們期待繼續與Apache Doris社區和中臺OLAP團隊在京東廣告場景中開展緊密合作,共同探索存算分離架構在該場景中的實際應用。
作者 京東零售廣告產研部-投放平臺部-投放報表組