Presto 在字節跳動的內部實踐與優化
在字節跳動內部,Presto 主要支撐了 Ad-hoc 查詢、BI 可視化分析、近實時查詢分析等場景,日查詢量接近 100 萬條。本文是字節跳動數據平臺 Presto 團隊-軟件工程師常鵬飛在 PrestoCon 2021 大會上的分享整理。
在字節跳動內部,Presto 主要支撐了 Ad-hoc 查詢、BI 可視化分析、近實時查詢分析等場景,日查詢量接近 100 萬條。
? 功能性方面:完全兼容 SparkSQL 語法,可以實現用戶從 SparkSQL 到 Presto 的無感遷移;
? 性能方面:實現 Join Reorder,Runtime Filter 等優化,在 TPCDS1T 數據集上性能相對社區版本提升 80.5%;
? 穩定性方面:首先,實現了多 Coordinator 架構,解決了 Presto 集群單 Coordinator 沒有容災能力的問題,將容災恢復時間控制在 3s 以內;其次實現了基于 histogram 的靜態規則和基于運行時狀態的動態規則,可以有效進行集群的路由和限流;
? 可運維性方面:實現了 History Server 功能,可以支持實時追蹤單個 Query 的執行情況,總體觀察集群的運行狀況。
字節跳動 OLAP 數據引擎平臺 Presto 部署使用情況
過去幾年,字節跳動的 OLAP 數據引擎經歷了百花齊放到逐漸收斂,再到領域細分精細化運營優化的過程。
存儲方面離線數據主要存儲在 HDFS,業務數據以及線上日志類數據存儲在 MQ 和 Kafka。
計算引擎根據業務類型不同,Presto 支撐了 Ad-hoc 查詢、部分 BI 報表類查詢,SparkSQL 負責超大體量復雜分析及離線 ETL、Flink 負責流式數據清洗與導入。

為了處理日益增長的 Ad-hoc 查詢需求,在 2020 年,字節跳動數據平臺引入 Presto 來支持該類場景。
目前,整個 Presto 集群規模在幾萬 core,支撐了每天約 100 萬次的查詢請求,覆蓋了絕大部分的 Ad-hoc 查詢場景以及部分 BI 查詢分析場景。

圖注:字節跳動內部 Presto 集群部署架構圖
上圖是字節跳動內部 Presto 集群部署的架構,針對不同的業務需求拆分為了多個相互隔離的集群,每個集群部署多個 Coordinator,負責調度對應集群的 Worker。
接入層提供了統一的 Gateway,用以負責用戶請求的路由與限流。同時還提供了 History Server,Monitor System 等附屬組件來增加集群的可運維性與穩定性。
Presto 集群穩定性和性能提升

針對不同的業務場景以及查詢性能要求,我們將計算資源拆分為了相互獨立的 Presto 集群。
Gateway 負責處理用戶請求的路由,這部分功能主要通過靜態的路由規則來實現,路由規則主要包括允許用戶提交的集群以及降級容災的集群等。
為了更好的平衡不同集群之間的負載情況,充分有效的利用計算資源,后期又引入了動態的路由分流策略。該策略在做路由選擇的過程中會調用各個集群 Coordinator 的 Restful API 獲取各個集群的負載情況,選擇最優的集群進行路由調度。
通過靜態規則與動態策略相結合的方式,Gateway 在為用戶提供統一接入接口的情況下,也保證了集群之間工作負載的平衡。

Coordinator 節點是單個 Presto 集群的核心節點,負責整個集群查詢的接入與分發,因此它的穩定性直接影響到整個集群的穩定性。
在最初的部署中,每個 Presto 集群只能部署一個 Coordinator,當該節點崩潰的時候,整個集群大概會消耗幾分鐘的不可用時間來等待該節點的自動拉起。
為了解決這個問題,我們開發了多 Coordinator 的功能。該功能支持在同一個 Presto 集群中部署多個 Coordinator 節點,這些節點相互之間處于 active-active 備份的狀態。
主要實現思路是將 Coordinator 和 Worker 的服務發現使用 Zookeeper 來進行改造。
Worker 會從 Zookeeper 獲取到現存的 Coordinator 并隨機選取一個進行心跳上報,同時每個 Coordinator 也可以從 Zookeeper 感知到其他 Coordinator 的存在。
每個 Coordinator 負責存儲當前連接到的 Worker 的任務負載情況以及由它調度的查詢執行情況,同時以 Restful API 的形式將這些信息暴露出去;其他 Coordinator 在做任務調度的時候會通過這些 Restful API 獲取到整個集群的資源使用情況進行相應的任務調度。
目前多 Coordinator 機制已經在集群中上線使用了半年,將集群的不可用時間從幾分鐘降低到 3s 以內。

另一個影響 Presto 集群穩定性的重要因素是超大規模的查詢。
在 Ad-hoc 場景下,這種查詢是無法避免的,并且由于這種查詢會掃描非常多的數據或者生成巨大的中間狀態,從而長期占用集群的計算資源,導致整個集群性能下降。
為了解決這個問題,我們首先引入了基于規則以及代價的查詢時間預測。
基于規則的查詢時間預測主要會統計查詢涉及到的輸入數據量以及查詢的復雜程度來進行預測。
基于代價的查詢時間預測主要是通過收集在 Catalog 中的 Histogram 數據來對查詢的代價進行預測。
上述預測能夠解決部分問題,但是還是會存在一些預估不準的情況,為了進一步處理這些情況,我們引入了 Adaptive Cancel 功能。
該功能主要是在查詢開始執行后,周期性的統計查詢預計讀取的數據量以及已完成的任務執行時間來預測查詢整體的執行時間,對于預測超過閾值的查詢提前進行取消,從而避免計算資源浪費,提升集群穩定性。

另外,Presto 本身提供的 UI 界面可以很好地對查詢執行情況進行分析,但是由于這部分信息是存儲在 Coordinator 內存當中,因此會隨著查詢數量的累積而逐步清除,從而導致歷史查詢情況無法獲取。
為了解決這個問題,我們開發了 History Server 的功能。
Coordinator 在查詢執行完成之后會將查詢的執行情況存儲到一個持久化存儲當中,History Server 會從持久化存儲當中加載歷史的查詢執行情況并提供與 Presto UI 完全相同的分析體驗,同時基于這部分持久化的信息,也可以建立相應的監控看板來觀測集群的服務情況。
三個場景優化的實踐分享
Ad-hoc 查詢分析場景
2020 年之前,大數據場景下的 Ad-hoc 查詢主要由 Hive/SparkSQL 來支撐。為了進一步優化查詢性能,提高資源使用效率,從 2020 年開始,我們在生產環境大規模使用 Presto。
? 與 SparkSQL 相比,Presto 是一個常駐的 MPP 架構的 SQL 查詢引擎,避免了 Spark Context 啟動以及資源申請的開銷,端到端延遲較低。
? 與 Hive/Spark Thrift Server 相比,Presto Coordinator 更加成熟,輕量,穩定,同時 Presto 基于全內存的 Shuffle 模型可以有效的降低查詢延遲。
為了做到用戶查詢無感遷移到 Presto,我們做了大量的工作使得 Presto 在語法和語義層面兼容 SparkSQL。

? 在接入層方面:提供了 SQL 標準化改寫功能。該功能可以將用戶的 SQL 改寫成 Presto 可以支持的 SQL 語法進行執行,做到了底層引擎對用戶透明。
? 在函數支持方面:在 Presto 中支持了 Hive UDF 的執行,使得之前數據分析師積累下來的大量 UDF 可以在 Presto 中執行。該功能主要支持了在解析階段可以加載 Hive UDF 和 UDAF,并進行類型轉換使其適配 Presto 類型體系,最終封裝成 Presto 內置函數的形式進行執行。目前該功能部分已經貢獻回了 Presto 社區。??社區地址??
BI 可視化分析場景
Presto 在字節跳動應用的另一個比較重要的場景是 BI 可視化分析。
BI 可視化分析提供了可視化交互的功能來進行數據分析,數據分析可以直觀快速的進行數據分析并生成相應的分析圖表,這給查詢引擎提出了更高的要求。在這一場景下,不僅,QPS 大幅提高,同時還要求查詢引擎能給出比較低的查詢延遲。
為了應對這些挑戰,我們做了一個比較重要的工作——在 Presto 中引入了物化視圖。
這種場景下,查詢 SQL 往往都是由 BI 可視化平臺根據固定的模版自動生成的,用戶的可視化操作往往限于對查詢過濾條件,聚合維度以及聚合指標的改變,適合物化視圖的應用。

在物化視圖功能中,我們借鑒了很多傳統數據庫的經驗,工作主要涉及三方面的工作:
? 物化視圖的自動挖掘——主要根據用戶查詢的歷史記錄進行分析,統計不同數據的查詢頻率進行物化視圖的自動推薦與創建。
? 物化視圖的生命周期管理——主要維護分區級別物化視圖的自動更新,刪除。
? 物化視圖的重寫功能——基于已有的物化視圖,對用戶的 query 進行重寫以減少查詢執行的復雜度。

近實時場景的查詢分析
這是今年開始探索的一個場景,主要是為了降低數據鏈路的延遲,提升查詢分析的時效性。
傳統的基于 ETL 的數據鏈路中,業務數據和日志數據經由 Kafka 定期 dump 到 HDFS,然后會有多個 ETL 任務對數據進行加工清理形成不同層級的 Hive 表用來進行查詢分析。
這個鏈路中往往需要進行表數據的全量更新,任務比較重,與線上數據存在 1 天以上的數據延遲。
為了降低數據延遲,我們引入了 Hudi 來進行數據的增量更新。

在這個鏈路中,業務數據和日志數據經由 Spark/Flink Streaming 任務增量寫入到 Hudi 表中,數據分析師可以直接查詢這部分數據。目前,該鏈路可以做到分鐘級別的數據延遲。
我們在 Presto 的優化工作主要是將 Hudi 表讀取的功能從 Hive Connector 中提取出來成為了一個單獨的 Hudi Connector。
? 首先,Hudi Connector 針對 Hudi 表的結構特點更好地支持了基于不同策略的分片調度算法,保證任務分配的合理性。
? 同時,Hudi Connector 優化了 Hudi MOR 表讀取過程中的內存管理,避免了 Worker 節點 OOM,提升了集群穩定性。
? 最后,Hudi Connector 的引入降低了 Hudi 版本升級帶來的工作量,可以更好的集成 Hudi 社區最新的功能。這部分功能我們將會逐步貢獻回社區。??社區地址??
本文中介紹的字節跳動內部 Presto 功能優化,目前已通過火山引擎數據產品“湖倉一體分析服務”向外部企業輸出。湖倉一體分析服務 LAS(Lakehouse Analytics Service)是面向湖倉一體架構的 Serverless 數據處理分析服務,提供一站式的海量數據存儲計算和交互分析能力,完全兼容 Spark、Presto、Flink 生態,幫助企業輕松完成數據價值洞察。
歡迎大家關注字節跳動數據平臺同名公眾號

浙公網安備 33010602011771號