Categories
程式開發

Presto 在有讚的實踐之路


本文主要介紹了 Presto 的簡單原理,以及 Presto 在有讚的實踐之路。

一、Presto 介紹

Presto 是由 Facebook 開發的開源大數據分佈式高性能 SQL 查詢引擎。起初,Facebook 使用 Hive 來進行交互式查詢分析,但 Hive 是基於 MapReduce 為批處理而設計的,延時很高,滿足不了用戶對於交互式查詢想要快速出結果的場景。為了解決 Hive 並不擅長的交互式查詢領域,Facebook 開發了 Presto,專門為交互式查詢所設計,提供分鐘級乃至亞秒級低延時的查詢性能。

1.1 Presto 架構

Presto 在有讚的實踐之路 1

1.2 Presto 執行查詢過程

  1. Client 發送請求給 Coordinator。
  2. SQL 通過 ANTLR 進行解析生成 AST。
  3. AST 通過元數據進行語義解析。
  4. 語義解析後的數據生成邏輯執行計劃,並且通過規則進行優化。
  5. 切分邏輯執行計劃為不同 Stage,並調度 Worker 節點去生成 Task。
  6. Task 生成相應物理執行計劃。
  7. 調度完後根據調度結果 Coordinator 將 Stage 串聯起來。
  8. Worker 執行相應的物理執行計劃。
  9. Client 不斷地向 Coordinator 拉取查詢結果,Coordinator 從最終匯聚輸出的 Worker 節點拉取查詢結果。

1.3 Presto 為何高性能

  • Pipeline, 全內存計算。
  • SQL 查詢計劃規則優化。
  • 動態代碼生成技術。
  • 數據調度本地化,注重內存開銷效率,優化數據結構,Cache,非精確查詢等其它技術。

二、Presto 在有讚的使用場景

Presto 在有讚的實踐之路 2

  • 數據平台(DP)的臨時查詢: 有讚的大數據團隊使用臨時查詢進行探索性的數據分析的統一入口,同時也提供了脫敏,審計等功能。
  • BI 報表引擎:為商家提供了各類分析型的報表。
  • 元數據數據質量校驗等:元數據系統會使用 Presto 進行數據質量校驗。
  • 數據產品:比如 CRM 數據分析,人群畫像等會使用 Presto 進行計算。

三、Presto 在有讚的演進之路

第一階段: Presto 和 Hadoop 混合部署階段:

起初,Presto 是和 Hadoop 離線集群混合在一起部署的。但是那時候用戶經常會抱怨 Presto 執行性能不穩定,對於同樣的 SQL,時快時慢。我們觀察到同樣的 Task,處理的數據量和花費的CPU Time 類似,但是有時候就會出現某些特別長的Elapsed Time的Task,從而拖慢整體的查詢性能。經分析,是因為在這個時間點磁盤 IO 帶寬被 Hadoop 離線任務打滿導致的。雖然 Hadoop 離線集群一般任務都會在凌晨進行調度,但是 也有一些任務會在白天不定期地跑,這種時候往往會比較影響性能。於是我們決定完全獨立 Presto 集群,並且單獨安裝 HDFS 環境。

第二階段: Presto 集群完全獨立階段:

我們準備將 Presto 單獨規劃出一個集群,並且單獨安裝 HDFS 環境,而離線 Hadoop 集群只需要將數據每天導入到這個 HDFS 環境中,此後離線 Hadoop 集群所有的任務都不會影響 Presto 集群。第一個問題就遇到了我們如何去將現有離線 Hadoop 集群的數據表導入到新的集群。目前我們的方案是共同使用一個 Hive,通過為專門新建一個庫,在創建庫的時候指定Location的方式去關聯到 Presto 集群的 HDFS NameService。後面用戶在這個庫下面建表就會將 Hive 表存儲到 Presto 集群。這時候我們的 Presto 性能就會相對穩定得多,基本不再會同樣的task處理差不多數據量的時候有幾個 Elapsed Time 特別高的情況了。

第三階段: 低延時業務專用 Presto 集群階段:

在第二階段我們的業務之間的資源隔離主要還是靠 Resource Group,但是這種隔離方式相對比較弱,不能提供細粒度的隔離,任務之間還是會互相影響。此外,不同業務的sql類型,查詢數據量,查詢時間,可容忍的 SLA,可提供的最優配置都是不一樣的。有些業務方需要一個特別低的響應時間保證,於是我們給這類業務部署了專門的集群去處理。部署在這個集群上的業務要求低延時,通常是3秒內,甚至有些能夠達到1秒內,而且會有一定量的並發。不過這類業務通常數據量不是非常大,而且通常都是大寬表,也就不需要再去Join 別的數據,Group By 形成的Group 基數和產生的聚合數據量不是特別大,查詢時間主要消耗在數據掃描讀取時間上。我們同樣也提供了資源完全獨立,具有本地 HDFS 的專用 Presto 集群給這類業務方去使用。此外,我們會為這種業務提供深度的性能測試,調整相應的配置,比如將 Task Concurrency 改成1,在並發量高的測試場景中,反而由於減少了線程間切換,性能會更好。

四、Presto 在有贊使用中的遇到的問題

4.1 HDFS 小文件問題

HDFS 小文件問題在大數據領域是個常見的問題。我們發現我們的數倉 Hive 表有些表的文件有幾千個,查詢特別慢。 Presto 這兩個參數限制了 Presto 每個節點每個 Task 可執行的最大 Split 數目。

node-scheduler.max-splits-per-node=100
node-scheduler.max-pending-splits-per-task=10

因此當查詢有許多小文件的表的時候,問題就爆發出來了,查詢起來特別慢。為了解決這個問題,我們分兩步走:

  • 適當調大了這兩個參數.
  • 在 Spark,Hive ETL 層面引入 Adaptive Spark 和小文件合併工具去解決這個小文件問題。

4.2 正則表達式指數級別回溯問題

有一天,有個用戶一個臨時查詢跑了1個小時也沒退出,通過jstack,找到了對應代碼,發覺是在運行Presto 裡面的正則表達式引擎Joni 庫匹配的代碼,後來發現他寫的是一個會產生指數級別回溯的正則表達式。社區的反饋是可以將 Presto 的正則表達式配置成 Google RE2J,但是 RE2J 會犧牲掉一些正則表達式的語法。後來我們儘管 Presto 是個多線程執行引擎,但是 Joni 引擎在設計上還是可以被 Interrupt 的,於是加上了查詢最大運行時間的限制,並且通知了相關的用戶。詳見

(https://github.com/prestodb/presto/issues/12191)

4.3 多個列 Distinct 的問題

有一些報表業務是使用 Presto 直接來算轉化率的,這樣的報表就會引起一個查詢語句中有多個 count distinct 列的問題。然而查看性能的時候會發覺這種語句特別慢,後來發覺,就算我手動將這個查詢語句分成多個語句,每個語句去執行一個 count distinct 時,也比合起來要快。於是深入調研了下,Spark,Hive TEZ,Calcite 之類的發覺 count distinct 在 SQL 優化器那邊會被優化掉,來解決數據傾斜的問題。

簡單來說:

單列的 count distinct:

select A, count(distinct B) from T group by A.

转换成

select A, count(B) from (select A, B from T group by A, B) group by A.

而多個 count distinct 列的原理類似,就是會使用 grouping sets 去將多個 group by 整合到一起來提升

   SELECT a1, a2,..., an, F1(b1), F2(b2), F3(b3), ...., Fm(bm), F1(distinct c1), ...., Fm(distinct cm) FROM Table GROUP BY a1, a2, ..., an
 
   转换为
 
   SELECT a1, a2,..., an, arbitrary(if(group = 0, f1)),...., arbitrary(if(group = 0, fm)), F(if(group = 1, c1)), ...., F(if(group = m, cm)) FROM
       SELECT a1, a2,..., an, F1(b1) as f1, F2(b2) as f2,...., Fm(bm) as fm, c1,..., cm group FROM
         SELECT a1, a2,..., an, b1, b2, ... ,bn, c1,..., cm FROM Table GROUP BY GROUPING SETS ((a1, a2,..., an, b1, b2, ... ,bn), (a1, a2,..., an, c1), ..., ((a1, a2,..., an, cm)))
       GROUP BY a1, a2,..., an, c1,..., cm group
   GROUP BY a1, a2,..., an

Presto 對於多個 count distinct 列這方面並沒有去實現。

這邊我們目前採用的方案是:

  • 修改代碼去實現,並且提交了 Issue 和 PR 給社區,一個被 merge 了,還有一個還在 review 中,後續還會繼續跟進。
    • Issue:[Optimizedistinctaggregationonmulticolumn([Optimizedistinctaggregationonmulticolumn([Optimizedistinctaggregationonmulticolumn([Optimizedistinctaggregationonmulticolumn(https://github.com/prestosql/presto/issues/613)
    • PR1: Fix Count(*) on empty relation returns NULL when optimizemixeddistinct_aggregation is turned on Merged
    • PR2:[Optimizedistinctaggregationonmultiplecolumns([Optimizedistinctaggregationonmultiplecolumns([Optimizedistinctaggregationonmultiplecolumns([Optimizedistinctaggregationonmultiplecolumns(https://github.com/prestosql/presto/pull/624) Reviewing
  • 讓業務方可以容忍非精確去重的選用 approxmate_distinct 去實現。

4.4 HDFS Namenode 導致有少數查詢會相對慢一點

在我們給用戶做專用presto集群獨立的性能測試時,我們發現同樣的SQL會有很少數查詢慢一點,後來研究了下發現 Presto Coordinator 去通過

public RemoteIterator listLocatedStatus(final Path f)

調用請求 HDFS NameNode 的時候,有時候會延遲1秒後返回。後來發覺這個時候正好是 NameNode 在做 Edit Log Rolling 的時候,由於這個時候 NameNode 會去拿讀寫鎖的寫鎖,從而阻塞了讀請求獲得讀鎖,因此有時候延遲1秒後返回。

這個問題目前由於基本可容忍,現階段也滿足了業務方的 SLA,所以後面沒有去解決:
我個人覺得,HDFS 並不是為了在線服務設計的,要提高 HDFS RPC 請求的穩定性,有以下幾種方式:

  • 參考[ Uber 引入了 Observer NameNode ]。 (https://eng.uber.com/scaling-hdfs/)
  • 使用 Alluxio,我們簡單測試了下 Alluxio,Alluxio Master 好像不會出現這種問題,在後面對未來的展望小節中,我們提到了 Alluxio + Presto 的意義。
  • 嘗試更換 NameNode 的盤為 SSD 盤,減少 Edit Log Rolling 的時間。

五、對未來的展望

5.1 Presto + Alluxio

Alluxio 通過能夠細粒度的去控制內存,會比純粹的靠 OS Page Cache 去控制頁級別緩存更具有優勢。你可以將一個表加載到 Alluxio 裡面,然後每次對它的訪問 IO 這塊花費的時間基本可以說是快速且恆定的。當然,我們也需要理性看待 Alluxio,從原理本質上來講,就 Presto 讀取數據這塊,這個要視情況而論。

我們測試過Presto 提供的HiveFileFormatBenchmark,大家也可以自己跑一下,結論就是單個CPU 核讀取TPCH 的一個Lineitem 表,ORC ZLIB 壓縮方式大概是在40MB/s, 當然不同數據格式,不同壓縮比會有所不同。因此,現代磁盤順序讀寫的速度可以達到150MB/s,如果就一個任務是不會有瓶頸的。這時候CPU 是瓶頸,但是現實是一個查詢多個任務跑,多個查詢並行跑,你這個時候就很難保證磁盤順序讀寫,吞吐,以及是否在OS Page Cache 中,這個時候就很有可能磁盤IO 是瓶頸了。因此 Alluxio 還是有用武之地的,至少可以把磁盤 IO 這個不可控因素給恆定下來。

5.2 Presto session property managers

新版本的 Presto 實現了 Session property manager 對於不同的 WorkLoad,不同的業務 SQL 類型,數據量,通過不同的配置能夠達到最好的效果。這個靠用戶自己去設置 Session Property 是不太現實的,必須在 Presto 服務端進行管理。

5.3 Presto多租戶隔離

目前Presto 官方並沒有實現和Apache Ranger 結合的多租戶隔離機制,我們目前有一個Sql Parser服務,去解析Presto,Hive,Spark 三種引擎的語法,去做脫敏,審計,智能選擇等功能,後面會去做結合Apache Ranger 通過sql 重寫來實現數據隔離,類似於現在的脫敏實現。

最後打個小廣告,有贊大數據團隊基礎設施團隊,主要負責有讚的數據平台(DP), 實時計算(Storm, Spark Streaming, Flink),離線計算(HDFS, YARN, HIVE, SPARK SQL),在線存儲(HBase),實時OLAP(Druid) 等數個技術產品,歡迎感興趣的小伙伴聯繫[email protected]

本文轉載自公眾號有贊coder(ID:youzan_coder)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA==&mid=2455760685&idx=1&sn=e5ebd75bf0c419cb2ff15322a3760f6a&chksm=8c686908bb1fe01ed226bead6b8a360928cef4dc1a2f7c4a026acc5329beee4fe2fe3755e520&scene=27#wechat_redirect