Alluxio Local Cache 加速 Presto 查詢在 Uber 的應用
背景
如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公眾號:過往記憶大資料
在 Uber,資料影響著每一個決定。Presto 是推動 Uber 各種資料分析的核心引擎之一。例如,運營團隊在儀表盤等服務中大量使用 Presto;Uber Eats 和營銷團隊依靠這些查詢的結果來決定價格。此外, Presto 還被用於 Uber 的合規部門、增長營銷部門和臨時資料分析。
如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公眾號:過往記憶大資料
Uber 的 Presto 規模很大。目前,Presto 有9000個日活躍使用者,每天處理500K次查詢,處理超過 50PB 的資料。Uber 的基礎設施包括兩個資料中心,7000個節點和跨越兩個地區的20個 Presto 叢集。
Uber 的 Presto 部署
當前架構
如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公眾號:過往記憶大資料
•UI/客戶端層:這包括內部儀表板、Google Data Studio、Tableau 和其他工具。此外,我們還有一些使用 JDBC 或查詢解析與 Presto 通訊的後端服務。•路由層:這一層負責將查詢分配到不同的 Presto 叢集。路由是基於從每個 Presto 叢集提取的統計資料,包括查詢和任務的數量、CPU 和記憶體使用情況等等。我們根據這些統計資訊確定每個查詢應該路由到哪個叢集。換句話說,這一層充當負載均衡和查詢攔截的服務。•Presto 叢集:在底部,多個 Presto 叢集與底層 Hive、HDFS、Pinot 等進行通訊。Join 操作可以在不同的 connectors 或不同的資料集之間執行。
此外,對於上述架構的每一層,我們有:
•內部監控•支援使用 Kerberos
Presto 工作負載分為兩類:
•互動式的:由資料科學家和工程師傳送的查詢;•定時任務:主要是定期迴圈的批次查詢,包括 Dashboard, ETL等。
使用 Alluxio 進行本地快取
最近,我們將 Alluxio 部署在我們三個生產環境的叢集中,每個叢集有200多個節點。我們使用的是 Alluxio Local Cache 模式,它利用 Presto worker 的本地 NVMe 磁碟。我們不是快取所有資料,而是透過選擇性快取其中一部分資料。
下圖是將 Alluxio 作為 Local Cache 的示意圖。Alluxio Cache Library 是一個執行在 Presto worker 內部的本地快取,我們在預設的 HDFS 客戶端之上實現了一個層。
當任何外部 API 從 HDFS 呼叫中讀取資料時,系統首先檢視快取,看看是否命中快取,如果命中,它將直接從本地 SSD 中讀取資料。否則,它將從遠端 HDFS 讀取資料,並在本地快取資料以備下一次讀取。在此過程中,快取命中率對整體效能有重要影響。
我們將在下面討論 Alluxio Local Cache 的詳細設計和改進。
主要挑戰和解決方案
挑戰1:實時分割槽更新
我們遇到的第一個挑戰是實時分割槽更新。在 Uber,很多表/分割槽都在不斷地變化,因為我們不斷地將資料插入 Hudi 表。
挑戰在於,僅使用分割槽 ID 作為快取鍵是不夠的。同一個分割槽可能在 Hive 中發生了變化,而 Alluxio 仍然快取過時的版本。在這種情況下,快取中的分割槽已經過時,因此如果資料來自快取,那麼在執行查詢時,使用者將得到過時的結果,從而導致不一致的體驗。
解決方法:將 Hive 的最新修改時間新增到快取 Key 中
我們的解決方案是為快取的 Key 新增最新的修改時間,如下所示:
•之前的快取 key 為: hdfs://<path>•現在的快取 Key 為:hdfs://<path><mod time>
Presto 目前可以透過 HDFS API 獲取每個 Hive 分割槽檔案的最新修改時間。具體來說,在處理 split 時,Presto worker 會顯式呼叫 HDFS listDirectory API,作為 HDFS 返回的資訊的一部分,有檔案的最新修改時間。透過此解決方案,快取了最新修改的新分割槽,確保使用者始終獲得其資料的一致檢視。注意,可能存在一個競態條件視窗,在 Presto worker 獲得最新的修改時間後,遠端檔案再次更新,而 Presto worker 仍然錯過最新的更改。一方面,在如此短的時間間隔內進行兩次連續更新是罕見的;另一方面,這樣的場景並不比沒有快取的情況差,但是在查詢執行期間更改了表目錄。在這種情況下,即使是現有的非快取執行也會導致不一致的行為。另一個注意事項是有一個權衡:過時的分割槽仍然存在於快取中,浪費快取空間,直到刪除。目前,我們正在努力改進快取清除策略。
挑戰2:叢集節點變更
在 Presto 中,Soft Affinity 排程是透過簡單的、基於求模的演算法實現的。該演算法的缺點是,如果新增或刪除一個節點,整個環將被不同的快取鍵打亂。因此,如果一個節點加入或離開叢集,它可能會損害所有節點的快取命中率,這是有問題的。
為了提高快取命中率,在 Presto 中讀取給定分割槽的資料會定位到相同的節點。雖然這很好,但問題是 Presto 之前使用了一個簡單的雜湊函式,當叢集發生變化時,這個函式可能會失效。
如下所示,目前,我們使用一個簡單的基於雜湊 mod 的節點查詢:key 4 % 3 nodes = worker#1。現在 worker#3 當機,新的查詢為:key 4 % 2 nodes = worker#0,但 worker#0 沒有快取相關的資料。
如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公眾號:過往記憶大資料
解決方案:基於節點 id 的一致性雜湊
一致雜湊(Consistent hashing)是解決方案。與基於求模的功能不同,所有節點都放在一個虛擬環上。無論節點加入或離開,環上節點的相對順序都不會改變。我們總是查詢環上的鍵,而不是使用求模得到的 hash 值。我們可以確保無論做了多少更改,它們總是基於相同的節點集。此外,我們使用複製來提高健壯性。這是解決叢集成員問題的解決方案。
挑戰3:快取大小限制
Uber 的資料湖規模比較大,Presto 叢集每天掃描50PB的資料。但是,我們的本地磁碟空間每個節點只有 500 GB。Presto 查詢訪問的資料量遠遠大於 Worker 節點上可用的磁碟空間。儘管可以將所有內容都放入快取中,但經常清理快取可能會損害整體快取效能。
解決方案: Cache Filter
其思想是隻快取選定的資料子集,其中包括某些表和一定數量的分割槽。解決方案是開發一個 cache filter,這是一種決定是否快取一張表以及快取多少個分割槽的機制。下面是一個配置示例:
如果想及時瞭解Spark、Hadoop或者HBase相關的文章,歡迎關注微信公眾號:過往記憶大資料
cache filter 大大提高了快取命中率,從~65%提高到>90%。下面是 cache filter 需要注意的幾個方面:
•它是手動、並且是靜態配置•需要根據訪問頻率進行設定•最常訪問的表;•需要根據訪問頻率進行設定•不經常更改的表•理想情況下,應該基於 shadow caching 的資料和表級指標。
我們還透過監控/儀表板實現了可觀察性,它與 Uber 的內部指標平臺整合,使用傳送到基於 grafana 的儀表板的 JMX 指標。
後設資料最佳化
在下面的小節中,我們將討論對本地快取後設資料的改進。
本地快取的檔案級後設資料
動機
首先,我們希望防止過時的快取。底層資料檔案可能由第三方框架更改。注意,這種情況在 Hive 表中可能很少見,但在 Hudi 表中很常見。
其次,每天從 HDFS 讀取的非複製資料可能很大,但我們沒有足夠的快取空間來快取所有資料。因此,我們可以透過為每個表設定配額來引入範圍配額管理。
第三,後設資料應該在伺服器重新啟動後可以恢復。我們將後設資料儲存在記憶體而不是磁碟中的本地快取中,這使得在伺服器關閉並重新啟動時不可能恢復後設資料。
高階別的方法
因此,我們提出檔案級後設資料(file-level metadata),它儲存檔案的最後修改時間和快取的每個資料檔案的範圍。檔案級後設資料儲存應該持久儲存在磁碟上,這樣資料才不會在重新啟動後消失。
隨著檔案級後設資料的引入,資料將有多個版本。當資料更新時,會生成一個新的時間戳,對應於一個新的版本。一個儲存新 page 的新資料夾將根據這個新時間戳建立。同時,我們將嘗試刪除舊的時間戳。
快取資料和後設資料結構
如上所示,我們有兩個資料夾對應兩個時間戳:timestamp1 和 timestamp2。通常,當系統執行時,不會同時有兩個時間戳,因為我們將刪除舊的 timestamp1,只保留 timestamp2。然而,在繁忙的伺服器或高併發性的情況下,我們可能無法刪除舊時間戳的資料,在這種情況下,我們可能同時有兩個時間戳的資料。此外,我們維護一個後設資料檔案,其中包含 protobuf 格式的檔案資訊和最新的時間戳。這確保了 Alluxio 的本地快取只從最新的時間戳讀取資料。當伺服器重新啟動時,從後設資料檔案中讀取時間戳資訊,以便正確管理配額和最後修改時間。
Metadata 感知
Cache Context
由於 Alluxio 是一種通用的快取解決方案,它仍然需要計算引擎(如 Presto)將後設資料傳遞給 Alluxio。為此,我們在 Presto 端實現了 HiveFileContext。對於 Hive 表或 Hud i表中的每個資料檔案,Presto 都會建立一個 HiveFileContext。在開啟 Presto 檔案時,Alluxio 會使用這些資訊。
當呼叫 openFile 時,Alluxio 建立一個 PrestoCacheContext 的新例項,它儲存 HiveFileContext,並具有作用域(4個級別:database, schema, table, partition)、quota、快取識別符號(即檔案路徑的 MD5 值)和其他資訊。我們將把這個 cache context 傳遞給本地檔案系統。因此,Alluxio 可以管理後設資料並收集指標。
Presto 側的每個查詢指標聚合
除了將資料從 Presto 傳遞到 Alluxio 之外,我們還可以回撥 Presto。在執行查詢操作時,我們將知道一些內部指標,例如有多少位元組的資料讀取到快取中,有多少位元組的資料從外部 HDFS 儲存中讀取。
如下所示,我們將包含 PrestoCacheContext 的 HiveFileContext 傳遞給本地快取檔案系統(LocalCacheFileSystem),之後本地快取檔案系統回撥(IncremetCounter)給 CacheContext。這個回撥鏈將繼續到 HiveFileContext,然後到 RuntimeStats。
在 Presto 中,RuntimeStats 用於在執行查詢時收集度量資訊,以便我們可以執行聚合操作。之後,我們可以在 Presto 的 UI 或 JSON 檔案中看到關於本地快取檔案系統的資訊。我們可以讓 Alluxio 和 Presto 在上述過程中緊密配合。在 Presto 方面,我們有更好的統計資料;在 Alluxio 方面,我們對後設資料有了更清晰的瞭解。
未來工作
下一步
首先,我們希望快取更多的表,並透過自動化改進表的快取過程,Alluxio Shadow Cache (SC) 將在這方面有所幫助。其次,我們希望對不斷變化的分割槽/Hudi 表有更好的支援。最後,負載均衡是我們可以實現的另一個最佳化。我們的旅程還有很長的路要走。
隨著計算-儲存分離以及大資料容器化繼續成為趨勢,我們相信像 Alluxio 這樣連線計算和儲存的統一層將繼續發揮關鍵作用。
效能調優
由於上述回撥過程使 CacheContext 的生命週期顯著增長,我們遇到了一些 GC 延遲上升的問題,我們正在努力解決。
Adopt Semantic Cache (SC)
我們將根據我們建議的檔案級後設資料來實現語義快取(SC)。例如,我們可以將資料結構儲存在 Parquet 或 ORC 檔案中。
更高效的反序列化
為了實現更有效的反序列化,我們將使用flatbuf而不是protobuf。儘管在ORC工廠中使用protobuf來儲存後設資料,但我們發現在Alluxio與Facebook的合作中,ORC的後設資料帶來了超過20-30%的CPU使用總量。因此,我們計劃用flatbuf替換現有的protobuf來儲存快取和後設資料,預計這將顯著提高反序列化的效能。
為了實現更高效的反序列化,我們將使用 flatbuf 代替 protobuf。雖然在 ORC factory 中使用了 protobuf 來儲存後設資料,但我們發現在 Alluxio 與 Facebook 的合作中,ORC 的後設資料帶來了超過 20-30% 的總 CPU 使用率。因此,我們計劃用 flatbuf 替換現有的 protobuf 來儲存快取和後設資料,這有望顯著提高反序列化的效能。
總結
在本文中,我們討論了 Uber Presto 快取解決方案的設計和實現,以提高 Uber 在各種用例中的互動式查詢效能。我們分享了 Presto 在 Uber 採用 Alluxio Local Cache 的歷程,討論了我們如何定製和擴充套件現有解決方案,以解決我們遇到的特定於 Uber 規模和用例的挑戰。該解決方案已在生產環境中執行超過四分之一,並且維護開銷最小。
本文翻譯自:《Speed Up Presto at Uber with Alluxio Local Cache》https://www.uber.com/blog/speed-up-presto-with-alluxio-local-cache/
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2925882/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Presto+Alluxio 加速 Iceberg 資料湖訪問RESTUX
- Alluxio在多級分散式快取系統中的應用UX分散式快取
- 提升50%!Presto如何提升Hudi表查詢效能?REST
- Presto + Alluxio:B站資料庫系統效能提升實踐RESTUX資料庫
- 秒級查詢之開源分散式SQL查詢引擎Presto實操-上分散式SQLREST
- Alluxio 助力 Kubernetes,加速雲端深度學習UX深度學習
- 給 Java 和 Android 構建一個簡單的響應式Local CacheJavaAndroid
- HBase查詢優化之Short-Circuit Local Reads優化UI
- mysql常見的查詢語句的應用MySql
- springboot應用查詢城市天氣Spring Boot
- Guava Cache本地快取在 Spring Boot應用中的實踐Guava快取Spring Boot
- FFmpeg應用實踐之命令查詢
- 在windows的IDEA執行PrestoWindowsIdeaREST
- SQL 查詢 exist join in 的用法和相應的適用場景 (最佳化查詢)SQL
- B站基於快取最佳化 PRESTO 叢集查詢效能快取REST
- 李呈祥:bilibili在湖倉一體查詢加速上的實踐與探索
- Grafana Loki查詢加速:如何在不新增資源的前提下提升查詢速度GrafanaLoki
- 分頁查詢及其擴充應用案例
- 透過遞迴查詢應用依賴遞迴
- 通過bundle Id查詢應用資訊
- Presto在滴滴的探索與實踐REST
- HTML Application Cache 離線應用HTMLAPP
- 在 with 查詢中只查詢個別欄位
- 用 RoadRunner 加速 Laravel 應用Laravel
- MySQL全面瓦解12:連線查詢的原理和應用MySql
- Iceberg 資料治理及查詢加速實踐
- 在Sqlalchemy的查詢中新共享filterSQLFilter
- 理解笛卡爾積在資料庫查詢中的實際應用與最佳化資料庫
- 查詢提速 20 倍,Apache Doris 在 Moka BI SaaS 服務場景下的應用實踐Apache
- 使用nodejs應用查詢SAP HANA Express Edition裡的資料NodeJSExpress
- 使用PHP應用查詢SAP HANA Express Edition裡的資料PHPExpress
- Excel 2010 SQL應用032 字元範圍的模糊匹配查詢ExcelSQL字元
- 使用機器學習加速對非結構化資料的查詢-第1部分(使用BlazeIt加速聚合和限制查詢)機器學習
- 並查集在實際問題中的應用並查集
- mac查詢埠對應的PIDMac
- 在Linux中,如何查詢系統中佔用CPU最高的程序?Linux
- 乾貨 | 每天十億級資料更新,秒出查詢結果,ClickHouse在攜程酒店的應用
- 順序表應用6:有序順序表查詢