這篇論文是寫(xiě)于2018年,并發(fā)表在ICDE 2019上,使用的版本還是0.211版本。當(dāng)時(shí)Presto還沒(méi)分裂出PrestoDB和PrestoSQL,目前他們的最新版本分別是0.223和331。但是不管怎么變,目前來(lái)看其內(nèi)核還是沒(méi)有太大的變化,所以這篇論文對(duì)于我們了解Presto以及其內(nèi)核還是有非常大的參考價(jià)值的。
由于個(gè)人水平有限,文中如果有讀起來(lái)拗口或者翻譯出錯(cuò)的地方,歡迎指正并參考原文。
原論文地址:https://research.fb.com/publications/presto-sql-on-everything/
Ⅰ. 引言
Presto作為一個(gè)分布式查詢引擎,于2013年開(kāi)始就已經(jīng)在Facebook的生產(chǎn)環(huán)境中使用。并且如今已經(jīng)在Uber、Netflix、Airbnb、Bloomberg以及LinkedIn這樣的大公司中使用。像Qubole、Treasure Data、Starburst Data等公司也提供了基于Presto的商業(yè)版產(chǎn)品,而Amazon的交互式查詢產(chǎn)品Athena也是基于Presto的。目前Presto社區(qū)有數(shù)百名contributors,是一個(gè)活躍度很高的開(kāi)源社區(qū)。
Presto具有自適應(yīng)、靈活以及可擴(kuò)展等特性。Presto提供了標(biāo)準(zhǔn)的ANSI SQL接口來(lái)查詢存儲(chǔ)于各系統(tǒng)中的數(shù)據(jù),如Hadoop、RDBMS、NoSQL數(shù)據(jù)庫(kù)中的數(shù)據(jù),以及Kafka這樣的流式組件中的數(shù)據(jù)(Presto中內(nèi)置了非常多的connectors供用戶使用)。Presto對(duì)外提供了開(kāi)放式的HTTP API、提供對(duì)JDBC的支持并且支持商業(yè)標(biāo)準(zhǔn)的BI的查詢工具(如Tableau)。其內(nèi)置的Hive connector源生支持對(duì)HDFS或Amazon S3上的文件進(jìn)行讀寫(xiě),并且支持多種流行的開(kāi)源文件格式,包括ORC、Parquet以及Avro。
截止到2018年末,在Facebook, Presto承擔(dān)著大量和sql分析相關(guān)的工作,包括即席的BI查詢以及長(zhǎng)周期的ETL任務(wù)。此外,Presto還助力于多款面向終端用戶的分析工具,提供高性能的數(shù)據(jù)展板、對(duì)內(nèi)部NoSQl系統(tǒng)的sql支持以及對(duì)諸如Facebook的A/B測(cè)試這樣的基礎(chǔ)組件提供支持??傊?,在Facebook,Presto每天正在處理著數(shù)百PB的數(shù)據(jù)。
Presto包含如下顯著的特性:
它是一個(gè)自適應(yīng)的多租戶系統(tǒng),可以同時(shí)運(yùn)行數(shù)百個(gè)資源(主要指Memory、IO、CPU等)敏感型的任務(wù),Presto將這些查詢?nèi)蝿?wù)分發(fā)到數(shù)百臺(tái)worker節(jié)點(diǎn)上,從而有效的利用集群資源。
Presto是可擴(kuò)展的聯(lián)邦查詢?cè)O(shè)計(jì)架構(gòu),允許管理人員在集群中通過(guò)一個(gè)查詢處理多個(gè)不同數(shù)據(jù)源的數(shù)據(jù)。這樣降低了集成多個(gè)查詢系統(tǒng)的復(fù)雜度。
它非常的靈活,可以通過(guò)配置不同的配置項(xiàng)提供對(duì)不同用例的支持。
Presto就是為高性能而生的,集成了多種優(yōu)化措施,包括代碼生成時(shí)的優(yōu)化。多個(gè)查詢共用一個(gè)長(zhǎng)期存活的JVM進(jìn)程,這樣可以減少響應(yīng)時(shí)間,但是這些都需要查詢調(diào)度以及資源管理和隔離等措施的支持。
這篇論文的主要作用就是向大家介紹Presto引擎的設(shè)計(jì)架構(gòu)并且討論特定的優(yōu)化措施,以及如何平衡這些優(yōu)化措施來(lái)達(dá)到上述特性。
Ⅱ. 使用示例
在Facebook,運(yùn)行著許多的Presto集群(多達(dá)上千個(gè)節(jié)點(diǎn))用來(lái)支持不同的使用場(chǎng)景。這部分會(huì)列舉4種不同的應(yīng)用案例。
A. Interactive Analytics(交互式分析)
Facebook內(nèi)運(yùn)行著一個(gè)龐大的多租戶數(shù)據(jù)倉(cāng)庫(kù),一些業(yè)務(wù)部門(mén)或個(gè)別團(tuán)隊(duì)會(huì)共享其中一小部分托管的集群。其數(shù)據(jù)存儲(chǔ)在一個(gè)分布式文件系統(tǒng)之上,而元數(shù)據(jù)則存儲(chǔ)在單獨(dú)的服務(wù)中,這些系統(tǒng)分別具有HDFS和Hive Metastore服務(wù)類似的API。我們稱之為'Facebook data warehouse',并且通過(guò)類似于Presto 'Hive' Connector的組件來(lái)進(jìn)行文件的讀寫(xiě)。
Facebook的工程師和數(shù)據(jù)科學(xué)家經(jīng)常會(huì)檢索少量的數(shù)據(jù)(50GB-3TB的壓縮數(shù)據(jù)),用來(lái)驗(yàn)證假設(shè),并構(gòu)建可視化的數(shù)據(jù)展板。這些用戶通常會(huì)使用查詢工具、BI工具或Jupyter notebooks來(lái)進(jìn)行查詢操作。各個(gè)群集需要支持50-100個(gè)具有各種查詢模型的操作并發(fā)執(zhí)行,并且需要在數(shù)秒或數(shù)分鐘內(nèi)返回結(jié)果。這些用戶通常并不關(guān)心查詢所使用到的硬件資源,但是對(duì)查詢時(shí)間卻相當(dāng)敏感。而對(duì)于某些探索性的查詢,用戶可能并不需要獲取所有的查詢結(jié)果。通常在返回初始結(jié)果后,查詢就會(huì)被立即取消或者用戶會(huì)通過(guò)LIMIT來(lái)限制系統(tǒng)返回的結(jié)果。
B. Batch ETL (批量ETL)
上面我們介紹到的數(shù)據(jù)倉(cāng)庫(kù)會(huì)使用ETL查詢?nèi)蝿?wù)定期填充新的數(shù)據(jù)。查詢?nèi)蝿?wù)通常是通過(guò)一個(gè)工作流系統(tǒng)依次調(diào)度執(zhí)行的。Presto支持用戶從歷史遺留的批處理系統(tǒng)遷移ETL任務(wù),目前ETL查詢?nèi)蝿?wù)在Facebook的Presto工作負(fù)載中占了很大一部分。這些查詢通常是由數(shù)據(jù)工程師開(kāi)發(fā)并優(yōu)化的。相對(duì)于Interactive Analytics中涉及的查詢,它們通常會(huì)占用更多的硬件資源,并且會(huì)涉及大量的CPU轉(zhuǎn)換和內(nèi)存(通常是數(shù)TB的分布式內(nèi)存)密集型的聚合操作以及與其他的大表連接操作。因此相對(duì)于資源利用率以及集群吞吐量來(lái)說(shuō),查詢延遲顯得沒(méi)那么重要。
C. A/B Testing (A/B測(cè)試)
Facebook使用A/B測(cè)試,通過(guò)統(tǒng)計(jì)假設(shè)性的測(cè)試來(lái)評(píng)估產(chǎn)品變更帶來(lái)的影響。在Facebook大量的A/B測(cè)試的基礎(chǔ)架構(gòu)是基于Presto構(gòu)建的。用戶期望測(cè)試結(jié)果可以在數(shù)小時(shí)之內(nèi)呈現(xiàn)(而不是數(shù)天),并且結(jié)果應(yīng)該是準(zhǔn)確無(wú)誤的。對(duì)于用戶來(lái)說(shuō),能夠在交互式延遲的時(shí)間內(nèi)(5~30s),對(duì)結(jié)果數(shù)據(jù)進(jìn)行任意切分來(lái)獲得更深入的見(jiàn)解同樣重要。而通過(guò)預(yù)處理來(lái)聚合這些數(shù)據(jù)往往很難滿足這一需求,因此必須得實(shí)時(shí)進(jìn)行計(jì)算。生成這樣的結(jié)果需要關(guān)聯(lián)多個(gè)大型數(shù)據(jù)集,包括用戶、設(shè)備、測(cè)試以及事件屬性等數(shù)據(jù)。由于查詢是通過(guò)編程方式實(shí)現(xiàn)的,所以查詢需要被限制在較小的集合內(nèi)。
D. Developer/Advertiser Analytics(開(kāi)發(fā)者/廣告主分析)
為外部開(kāi)發(fā)者和廣告客戶提供的幾種自定義報(bào)表工具也都是基于Presto構(gòu)建的。Facebook Analytics就是其中一個(gè)實(shí)際案例,它為使用Facebook平臺(tái)構(gòu)建應(yīng)用程序的開(kāi)發(fā)人員提供了高級(jí)的分析工具。這些工具通常對(duì)外開(kāi)放一個(gè)Web界面,該界面可以生成一組受限的查詢模型。查詢需要聚合的數(shù)據(jù)量是非常大的,但是這些查詢是有目的性的,因?yàn)橛脩糁荒茉L問(wèn)他們的應(yīng)用程序或廣告的數(shù)據(jù)。大部分的查詢包括連接、聚合以及窗口函數(shù)。由于這些工具是交互式的,因此有非常嚴(yán)格的時(shí)間限制(約50ms~5s)。鑒于用戶的數(shù)量,集群需要達(dá)到99.999%的可用性,并且支持?jǐn)?shù)百個(gè)并發(fā)查詢。
Ⅲ. 架構(gòu)概覽
一個(gè)Presto集群需要由一個(gè)Coordinator以及一個(gè)或者多個(gè)Worker節(jié)點(diǎn)組成。Coordinator主要負(fù)責(zé)接收查詢請(qǐng)求、解析語(yǔ)句、生成計(jì)劃、優(yōu)化查詢以及查詢調(diào)度。Worker節(jié)點(diǎn)主要負(fù)責(zé)查詢處理。如下所示的即為Presto架構(gòu):

客戶端向Coordinator發(fā)送一個(gè)包含sql的http請(qǐng)求。Coordinator接收到這個(gè)請(qǐng)求,會(huì)通過(guò)評(píng)估隊(duì)列策略,解析和分析sql文本,創(chuàng)建和優(yōu)化分布式執(zhí)行計(jì)劃來(lái)處理請(qǐng)求。
Coordinator將執(zhí)行計(jì)劃分發(fā)給Worker節(jié)點(diǎn),接著Worker節(jié)點(diǎn)開(kāi)始啟動(dòng)tasks并且開(kāi)始枚舉splits,而這些splits是對(duì)外部存儲(chǔ)系統(tǒng)中可尋址的數(shù)據(jù)塊的一種隱晦處理。Splits會(huì)被分配給那些負(fù)責(zé)讀取數(shù)據(jù)的tasks。
Worker節(jié)點(diǎn)運(yùn)行這些tasks來(lái)處理從外部存儲(chǔ)系統(tǒng)獲取的splits,以及來(lái)自于其他Worker節(jié)點(diǎn)處理過(guò)得中間數(shù)據(jù)。Worker節(jié)點(diǎn)之間通過(guò)多任務(wù)合作機(jī)制來(lái)并發(fā)處理來(lái)自不同查詢的tasks。任務(wù)盡可能的以流水線的方式來(lái)執(zhí)行,這使得數(shù)據(jù)可以在tasks之間進(jìn)行流動(dòng)。對(duì)于某些特定的查詢,Presto能夠在處理完所有數(shù)據(jù)之前就返回結(jié)果。中間數(shù)據(jù)以及狀態(tài)會(huì)盡可能地存儲(chǔ)在內(nèi)存中。當(dāng)在節(jié)點(diǎn)之間對(duì)數(shù)據(jù)進(jìn)行shuffle時(shí),Presto會(huì)調(diào)整緩沖區(qū)來(lái)達(dá)到最小化的延遲。
Presto被設(shè)計(jì)成可擴(kuò)展的,并提供通用的插件接口。插件可以提供自定義的數(shù)據(jù)類型、函數(shù)、訪問(wèn)控制、事件監(jiān)聽(tīng)策略、排隊(duì)策略以及屬性配置。更重要的是插件還提供了connector,這使得Presto可以通過(guò)Connector API和外部的存儲(chǔ)系統(tǒng)進(jìn)行通信。Connector API主要包含如下四個(gè)部分:Metadata API、Data Location API、Data Source API以及Data Sink API。這些API可以幫助在分布式查詢引擎中實(shí)現(xiàn)高性能的connectors,開(kāi)發(fā)者已經(jīng)為Presto社區(qū)提供了數(shù)十個(gè)connector,而且我們也注意到了一些專有的connectors。
Ⅳ. 系統(tǒng)設(shè)計(jì)
這一部分將介紹Presto引擎的一些關(guān)鍵設(shè)計(jì)和特性。首先,我們會(huì)介紹Presto支持的SQL方言,以及查詢從客戶端到分布式執(zhí)行的整個(gè)生命周期。接著會(huì)介紹Presto中支持的多租戶的資源管理機(jī)制。最終,會(huì)簡(jiǎn)短的討論一下容錯(cuò)機(jī)制。
A. SQL 方言
Presto嚴(yán)格遵守標(biāo)準(zhǔn)的ANSI SQL規(guī)范。雖然Presto并未實(shí)現(xiàn)所有的SQL語(yǔ)義,但是所提供的功能都會(huì)盡可能的符合規(guī)范。為了提高可用性,我們對(duì)SQL語(yǔ)義做了一些精心的擴(kuò)展。例如,在ANSI SQL中實(shí)現(xiàn)map、array等操作是相當(dāng)復(fù)雜的。為了簡(jiǎn)化對(duì)這些數(shù)據(jù)類型的處理,Presto在語(yǔ)法上支持匿名函數(shù)(lamada表達(dá)式)并且內(nèi)置了一些高階的函數(shù)(如 transform, filter, reduce)。
B. 客戶端接口,SQL解析以及查詢計(jì)劃
1)Client接口:Coordinator對(duì)客戶端提供了REST API并且配備了命令行接口。Presto同樣配備了JDBC客戶端,以此來(lái)兼容大量的BI工具,包括Tableau和Microstrategy。
2)語(yǔ)法解析:Presto使用基于ANTLR的解析器將sql轉(zhuǎn)換成相應(yīng)的語(yǔ)法樹(shù)。解析器通過(guò)這棵語(yǔ)法樹(shù)來(lái)確定類型、解析函數(shù)和作用域,并且抽取如子查詢、聚合以及窗口函數(shù)等邏輯部分。
3)邏輯計(jì)劃:邏輯計(jì)劃器使用語(yǔ)法樹(shù)和分析信息來(lái)生成計(jì)劃節(jié)點(diǎn)的樹(shù)形編碼的中間表示形式。每一個(gè)計(jì)劃節(jié)點(diǎn)都表示一個(gè)物理或邏輯操作,而計(jì)劃節(jié)點(diǎn)的子節(jié)點(diǎn)則為當(dāng)前節(jié)點(diǎn)提供輸入數(shù)據(jù)。邏輯計(jì)劃器生成的節(jié)點(diǎn)是純粹的邏輯節(jié)點(diǎn),并不包含任何執(zhí)行信息。例如下面的一個(gè)簡(jiǎn)單查詢:
SELECT
orders.orderkey, SUM(tax)
FROM orders
LEFT JOIN lineitem
ON orders.orderkey = lineitem.orderkey
WHERE discount = 0
GROUP BY orders.orderkey
如上查詢生成的邏輯計(jì)劃如下所示:

C. 查詢優(yōu)化
計(jì)劃優(yōu)化器會(huì)將邏輯計(jì)劃轉(zhuǎn)換成能夠表示有效執(zhí)行策略的物理結(jié)構(gòu)。轉(zhuǎn)換過(guò)程是通過(guò)貪婪地評(píng)估一組轉(zhuǎn)換規(guī)則,直到達(dá)到某一特定目標(biāo)。每個(gè)規(guī)則都會(huì)匹配一種模式,該模式可以與查詢計(jì)劃的子樹(shù)匹配,并確定是否需要轉(zhuǎn)換。轉(zhuǎn)換的結(jié)果是一個(gè)邏輯上等價(jià)的子計(jì)劃,該子計(jì)劃會(huì)替換樹(shù)中滿足轉(zhuǎn)換規(guī)則的節(jié)點(diǎn)。Presto包含了一些通用的的優(yōu)化規(guī)則,如謂詞和限制下推、列裁剪以及去相關(guān)(decorrelation)。
我們正在使用基于成本的計(jì)劃評(píng)估,來(lái)增強(qiáng)優(yōu)化器以對(duì)搜索空間進(jìn)行更全面的探索,該技術(shù)是從Cascades框架引入的。而目前Presto已經(jīng)支持了兩種基于基于成本的優(yōu)化,這些優(yōu)化策略考慮了表和列的統(tǒng)計(jì)信息,如關(guān)聯(lián)策略選擇以及關(guān)聯(lián)重排。這里我們只會(huì)介紹優(yōu)化器的部分功能,而詳細(xì)的優(yōu)化策略已經(jīng)超出了本文的范圍。
1)數(shù)據(jù)分布:Connector提供了數(shù)據(jù)分布的API,而優(yōu)化器可以很好的利用數(shù)據(jù)的物理布局。Connector可以提供數(shù)據(jù)位置信息以及分區(qū)、排序、分組和索引等信息。Connector可以返回一張表的多個(gè)物理數(shù)據(jù)分布情況,每部分的數(shù)據(jù)具有不同的屬性, 優(yōu)化器可以從中選擇對(duì)當(dāng)前查詢最有效的數(shù)據(jù)。該項(xiàng)功能已經(jīng)被很好的運(yùn)用在 Developer/Advertiser Analytics的案列中;該系統(tǒng)的超級(jí)管理員們可以通過(guò)獲取數(shù)據(jù)的物理分布來(lái)優(yōu)化不同的查詢。在隨后的部分中,我們將看到Presto引擎是如何利用這些屬性的。
2)謂詞下推:優(yōu)化器可以與Connector一起使用,用來(lái)確定下推的范圍以及等效的謂詞下推,從而提高過(guò)濾效率。
例如,在Developer / Advertiser Analytics的案列中,在MySQL分片之上構(gòu)建了專有的Connector。Connector將存儲(chǔ)在Mysql實(shí)例上的數(shù)據(jù)劃分成小的數(shù)據(jù)分片,并且將下推范圍以及關(guān)鍵謂詞應(yīng)用于這些數(shù)據(jù)分片,以此來(lái)保證從Mysql中讀取滿足需求的數(shù)據(jù)。如果存在多種數(shù)據(jù)分布,引擎會(huì)選擇已在謂詞列上構(gòu)建索引的那部分?jǐn)?shù)據(jù)。在Developer / Advertiser Analytics的案列中,基于索引的高效過(guò)濾是非常重要的。而在Interactive Analytics和Batch ETL的案例中,Presto會(huì)利用Hive connector中的分區(qū)修剪和文件格式的特性以類似的方式提查詢高性能。
3)節(jié)點(diǎn)間并行: 確定執(zhí)行計(jì)劃中的哪些部分可以在Worker節(jié)點(diǎn)之間并行處理,也屬于優(yōu)化工作的一部分。執(zhí)行計(jì)劃中可以被并行處理的部分稱之為stage,每個(gè)stage都會(huì)被分配給一個(gè)或多個(gè)task,而每個(gè)task對(duì)不同的輸入數(shù)據(jù)集執(zhí)行相同的計(jì)算。引擎在stages之間進(jìn)行shuffles以實(shí)現(xiàn)數(shù)據(jù)交換。Shuffles會(huì)增加了延遲、耗盡緩沖區(qū)內(nèi)存并造成CPU的高負(fù)載。因此,優(yōu)化器需要慎重考慮計(jì)劃中被引入的shuffles總數(shù)。如圖3所示,將計(jì)劃劃分成不同的stage,并通過(guò)shuffles連接不同的stage。
數(shù)據(jù)分布屬性: 優(yōu)化器可以使用數(shù)據(jù)的物理布局來(lái)最小化shuffle的次數(shù)。這在A/B Testing 的案列中非常有用。在該場(chǎng)景下,幾乎每一個(gè)查詢都需要通過(guò)連接大量的數(shù)據(jù)才能產(chǎn)生結(jié)果??紤]到參與連接的兩張表都是作用于同一字段,引擎會(huì)利用該特點(diǎn),將同時(shí)位于本地的列數(shù)據(jù)進(jìn)行關(guān)聯(lián)從而消除耗資源的shuffle操作。
如果Connector向外提供了連接列的數(shù)據(jù)分布情況,那么參與連接的列會(huì)被標(biāo)記成索引。這樣,優(yōu)化器就能確定本地的基于索引嵌套循環(huán)的連接是否是一個(gè)好的優(yōu)化策略。結(jié)合生產(chǎn)數(shù)據(jù)的存儲(chǔ)方式(鍵值存儲(chǔ)或其它方式的存儲(chǔ)),這種優(yōu)化措施可以高效的應(yīng)用于數(shù)據(jù)倉(cāng)庫(kù)中的標(biāo)準(zhǔn)化數(shù)據(jù)。在Interactive Analytics的案列中該優(yōu)化措施會(huì)被經(jīng)常用到。
節(jié)點(diǎn)屬性:如Connector一樣,計(jì)劃樹(shù)中的節(jié)點(diǎn)也包含了其輸出數(shù)據(jù)的各種屬性信息(如分區(qū),排序,分桶以及分組等數(shù)據(jù)特性)。這些節(jié)點(diǎn)還包含了一些必要和首選的屬性,在進(jìn)行數(shù)據(jù)的shuffle時(shí)這些信息都可以被考慮在內(nèi)。多余的shuffle會(huì)被刪除,但是在其他的一些情況下,可以通過(guò)更改shuffle的屬性來(lái)減少必要的shuffle數(shù)量。Presto會(huì)貪婪地選擇分區(qū),而這些分區(qū)將滿足盡可能多的必要屬性以減少shuffle數(shù)量。這意味著優(yōu)化器可以選擇在較少的列上進(jìn)行分區(qū),而這在某些情況下會(huì)導(dǎo)致更多的分區(qū)傾斜。例如,應(yīng)用于圖3中計(jì)劃的優(yōu)化措施,將導(dǎo)致多個(gè)stage合并到單個(gè)stage中去執(zhí)行。

4)節(jié)點(diǎn)內(nèi)并行:優(yōu)化器會(huì)使用類似的機(jī)制來(lái)確定執(zhí)行計(jì)劃的各個(gè)階段中哪些部分可以通過(guò)單節(jié)點(diǎn)上的多線程來(lái)提高執(zhí)行效率。與Worker節(jié)點(diǎn)間的并行處理相比,節(jié)點(diǎn)內(nèi)的并行處理效率會(huì)更高,因?yàn)楣?jié)點(diǎn)內(nèi)的延遲會(huì)更低,并且狀態(tài)(如哈希表和字典等)也可以在線程之間進(jìn)行高效的共享。添加節(jié)點(diǎn)內(nèi)的并行可以顯著的提高執(zhí)行效率,特別是對(duì)于那些并發(fā)限制了下游階段吞吐量的查詢:
在Interactive Analytics的案例中,會(huì)涉及到許多簡(jiǎn)短的一次性查詢,用戶通常不會(huì)花時(shí)間來(lái)優(yōu)化這些查詢。這樣就會(huì)導(dǎo)致分區(qū)傾斜時(shí)常發(fā)生,部分是由于數(shù)據(jù)的某些內(nèi)在屬性以及常見(jiàn)的查詢模式導(dǎo)致的(如將用戶按國(guó)家進(jìn)行分組,同時(shí)也過(guò)濾掉一小部分國(guó)家)。這通常表現(xiàn)為大量數(shù)據(jù)的哈希分區(qū)只會(huì)作用于少量的節(jié)點(diǎn)上。
在Batch ETL的案例中,作業(yè)任務(wù)經(jīng)常不進(jìn)行數(shù)據(jù)過(guò)濾或者很少的過(guò)濾,就直接對(duì)大量的數(shù)據(jù)集進(jìn)行轉(zhuǎn)換處理。在這些情況下,計(jì)劃樹(shù)中較高層級(jí)的節(jié)點(diǎn)所涉及到的Worker節(jié)點(diǎn)數(shù)可能較少,以至于無(wú)法快速處理由葉子階段(leaf stage)所產(chǎn)生的數(shù)據(jù)。這會(huì)涉及到任務(wù)調(diào)度,后面會(huì)介紹。
在如上的兩種情況下,在每個(gè)Worker節(jié)點(diǎn)上通過(guò)多線程處理可以在某種程度上緩解并發(fā)的瓶頸。引擎可以通過(guò)多線程來(lái)處理一個(gè)序列的算子(也稱為管道)。如圖4所示,優(yōu)化器通過(guò)并行處理來(lái)優(yōu)化連接操作。

D. 調(diào)度
Coordinator通過(guò)分發(fā)可執(zhí)行的task的方式,向Woker節(jié)點(diǎn)分配執(zhí)行計(jì)劃的各個(gè)stage,這些可執(zhí)行的task可以被看作單個(gè)處理單元。接著,Coordinator將一個(gè)stage的task與其他stage的task通過(guò)shuffles相連接,從而形成一個(gè)樹(shù)形的處理鏈路。只要各個(gè)stage都是可用的,數(shù)據(jù)就會(huì)在stage之間流轉(zhuǎn)。
一個(gè)task的內(nèi)部可能會(huì)存在多個(gè)管道(pipeline)。而一個(gè)pipeline又是一連串的算子(operator)組成的,而每一個(gè)operator都會(huì)對(duì)數(shù)據(jù)進(jìn)行單獨(dú)的處理。例如,執(zhí)行哈希聯(lián)接的任務(wù)必須包含至少兩個(gè)pipeline;一個(gè)用于構(gòu)建哈希表(build pipeline),而另一個(gè)則用于在探測(cè)端對(duì)流入的數(shù)據(jù)進(jìn)行連接操作(probe pipeline)。當(dāng)優(yōu)化器認(rèn)為pipeline的部分操作可以通過(guò)局部的并行處理來(lái)優(yōu)化時(shí),優(yōu)化器會(huì)對(duì)pipeline進(jìn)行拆分并單獨(dú)對(duì)這部分操作進(jìn)行并行化處理。圖4中展示了build pipeline被劃分成兩個(gè)pipeline,一個(gè)用于掃描數(shù)據(jù),而另一個(gè)則用于構(gòu)建局部哈希表。兩個(gè)pipeline之間則通過(guò)local shuffle連接起來(lái)。
為了執(zhí)行一個(gè)查詢,Presto引擎會(huì)制定一個(gè)兩階段的調(diào)度策略。首先,需要確定各個(gè)stage之間的調(diào)度順序。其次,需要確定有多少task會(huì)被調(diào)度,以及這些task需要被分配到哪些Woker節(jié)點(diǎn)上。
1)Stage 調(diào)度:在這一調(diào)度階段,Presto支持兩種調(diào)度策略:一次性調(diào)度(all-at-once)以及階段調(diào)度(phased)。一次性調(diào)度所有的stage并發(fā)執(zhí)行可以最小化CPU時(shí)鐘開(kāi)銷;一旦數(shù)據(jù)可用,就會(huì)被立即進(jìn)行處理。這種調(diào)度策略有利于那些對(duì)延遲敏的場(chǎng)景,如Interactive Analytics,Developer/Advertiser Analytics以及A/B Testing的案例。分階段調(diào)度的策略會(huì)首先確定有向數(shù)據(jù)流圖中所有必須緊密連接的單元,這些單元必須同時(shí)啟動(dòng)以避免死鎖,并且需要按照拓?fù)漤樞騺?lái)執(zhí)行。例如,如果在階段調(diào)度模式下執(zhí)行hash-join,則在構(gòu)建哈希表之前,都不會(huì)調(diào)度執(zhí)行hash-join左側(cè)的任務(wù)。這大大的提高了Batch Analytics案列中的內(nèi)存使用效率。
當(dāng)調(diào)度器根據(jù)調(diào)度策略確定某個(gè)stage應(yīng)該被調(diào)度時(shí),它會(huì)開(kāi)始將該stage內(nèi)的task分配給Worker節(jié)點(diǎn)來(lái)執(zhí)行。
2)Task調(diào)度:Task調(diào)度器通過(guò)檢查計(jì)劃樹(shù)會(huì)將stage劃分為葉子階段(leaf stages)或中間階段( intermediate stages)。葉子階段會(huì)通過(guò)connector來(lái)讀取數(shù)據(jù);而中間階段僅處理其他階段產(chǎn)生的中間結(jié)果。
葉子階段(leaf stage): 在葉子階段,Task調(diào)度器在將task分配給Worker節(jié)點(diǎn)時(shí),會(huì)考慮網(wǎng)絡(luò)以及連接器所帶來(lái)的影響。例如,無(wú)共享的部署方式會(huì)要求Worker節(jié)點(diǎn)和存儲(chǔ)節(jié)點(diǎn)在一起。在這種情況下,調(diào)度器會(huì)使用connector的Layout API來(lái)確定數(shù)據(jù)的分布方式從而確定task的執(zhí)行位置。在A/B Testing的案列中,需要可預(yù)測(cè)的高吞吐量以及低延遲的數(shù)據(jù)讀取,而Raptor connector可以滿足這些需求。Raptor是針對(duì)Presto優(yōu)化的存儲(chǔ)引擎,采用無(wú)共享架構(gòu),可將ORC文件存儲(chǔ)在閃存盤(pán)上,并在MySQL中存儲(chǔ)其元數(shù)據(jù)。
分析顯示,我們整個(gè)生產(chǎn)集群上的大部分CPU時(shí)間都用于對(duì)那些從connector讀取的數(shù)據(jù)進(jìn)行解壓縮,解碼,過(guò)濾和轉(zhuǎn)換。而這些工作是可以被高度并行執(zhí)行的,并且在盡可能多的節(jié)點(diǎn)上運(yùn)行這些stage通常會(huì)產(chǎn)生最短的掛墻時(shí)間(wall time)。因此,如果沒(méi)有加以限制,并且源數(shù)據(jù)可以被劃分為足夠多的split,葉子階段的task會(huì)在集群中所有的worker節(jié)點(diǎn)上執(zhí)行。對(duì)于以共享存儲(chǔ)模式(即所有數(shù)據(jù)都是遠(yuǎn)程存儲(chǔ)的)運(yùn)行的Facebook數(shù)據(jù)倉(cāng)庫(kù)而言,群集中的每個(gè)節(jié)點(diǎn)通常都會(huì)參與葉子階段的任務(wù)處理。但是這種執(zhí)行策略是網(wǎng)絡(luò)敏感性的。
調(diào)度器還可以使用插件提供的層次結(jié)構(gòu)來(lái)推理網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu),從而優(yōu)化讀取效率。在Facebook,網(wǎng)絡(luò)受限的部署方式可以利用這種機(jī)制幫助Presto引擎優(yōu)先讀取機(jī)架本地的數(shù)據(jù),而不是遠(yuǎn)程機(jī)架的數(shù)據(jù)。
中間階段(Intermediate Stages): 中間階段的task可以放在任意worker節(jié)點(diǎn)來(lái)運(yùn)行。但是,Presto引擎仍然需要確定每個(gè)stage應(yīng)計(jì)劃執(zhí)行多少個(gè)task。而這些是基于Connector的配置,執(zhí)行計(jì)劃中的屬性,被處理數(shù)據(jù)的分布以及其他部署配置所決定的。而在某些情況下,執(zhí)行引擎可以在執(zhí)行任務(wù)期間動(dòng)態(tài)更改任務(wù)數(shù)。
3)Split調(diào)度:當(dāng)葉子階段的task開(kāi)始在worker節(jié)點(diǎn)上執(zhí)行時(shí),該節(jié)點(diǎn)會(huì)接收一個(gè)或者多個(gè)data split。一個(gè)split包含的信息因Connector而異。當(dāng)從分布式文件系統(tǒng)讀取數(shù)據(jù)時(shí),split可能包含文件路徑和當(dāng)前文件的偏移量。對(duì)于Redis這種鍵值存儲(chǔ)系統(tǒng)來(lái)說(shuō),一個(gè)split會(huì)包含表信息,鍵和值格式以及要查詢的主機(jī)列表等。
處于葉子階段的每一個(gè)task需要分配到一個(gè)或者多個(gè)data split之后才能被執(zhí)行,而處于中間階段的task始終是可運(yùn)行的,并且僅在task被終止時(shí)或完成其所有上游task后才停下來(lái)。
分配分片(Split Assignment): 一旦tasks被分配到worker節(jié)點(diǎn)之后,Coordinator開(kāi)始將data split分配給這些tasks。Presto會(huì)請(qǐng)求Connector枚舉小批量的data split,然后將它們延遲(lazy)分配給這些tasks。這是Presto的一個(gè)重要功能,它帶來(lái)了許多的好處:
將查詢響應(yīng)時(shí)間和Connector枚舉大量的data split的時(shí)間解耦。例如,Hive-connector可能需要數(shù)分鐘來(lái)枚舉分區(qū)并列出每個(gè)分區(qū)目錄中的文件。
可以在不處理完所有數(shù)據(jù)的情況下就開(kāi)始生成查詢結(jié)果(例如,簡(jiǎn)單地使用過(guò)濾器來(lái)查詢數(shù)據(jù)),這些查詢操作通常會(huì)被快速的取消或者在滿足LIMIT子句時(shí)提早完成。在 Interactive Analytics的案列中,通常是在所有data split被枚舉完之前完成查詢。
Worker節(jié)點(diǎn)上都維護(hù)著一個(gè)將要被處理的data split隊(duì)列。Coordinator會(huì)簡(jiǎn)單的將data split分配給那些擁有最短split隊(duì)列的task。 保持這些隊(duì)列盡量的小,可以使系統(tǒng)能夠適應(yīng)處理不同split帶來(lái)的CPU成本差異以及worker節(jié)點(diǎn)間的性能差異。
允許在執(zhí)行查詢時(shí),不必將所有的元數(shù)據(jù)都保存在內(nèi)存中。這對(duì)于Hive-connector是至關(guān)重要的,因?yàn)樵趫?zhí)行查詢時(shí)Hive-connector可能會(huì)訪問(wèn)數(shù)百萬(wàn)個(gè)data split,這會(huì)導(dǎo)致Coordinator上所有可用的內(nèi)存很快的被消耗殆盡。
對(duì)于運(yùn)行在Facebook內(nèi)Hive兼容的數(shù)據(jù)倉(cāng)庫(kù)上的Interactive Analytics 和 Batch ETL的案例,這些特性會(huì)顯得格外的有用。 但是值得注意的是,data split的懶加載可能會(huì)導(dǎo)致精確估算和報(bào)告查詢進(jìn)度變得困難。
E. 查選執(zhí)行
1)本地?cái)?shù)據(jù)流:一旦data split分配給線程后,它會(huì)在driver中循環(huán)執(zhí)行。Presto的driver中的循環(huán)比流行的Volcano遞歸迭代器模型更復(fù)雜,但卻提供了重要的功能。它更適用于多任務(wù)的協(xié)作處理,因?yàn)樗阕樱╫perator)可以在線程生成之前就迅速進(jìn)入已知的狀態(tài),而不是無(wú)限地阻塞下去。此外,driver可以在不需要額外的輸入文件的情況下,通過(guò)移動(dòng)operator之間的數(shù)據(jù)使每個(gè)數(shù)據(jù)量子的執(zhí)行效率達(dá)到最大化(如恢復(fù)資源密集型的計(jì)算或爆炸性轉(zhuǎn)換的計(jì)算)。循環(huán)的每次迭代都會(huì)在operators之間移動(dòng)數(shù)據(jù)驅(qū)使查詢不斷的執(zhí)行。
drive循環(huán)中操作的數(shù)據(jù)單元稱之為page,而一個(gè)page為某一列一串行值編碼后的數(shù)據(jù)。Connector的Data Source API在傳入一個(gè)data split之后會(huì)返回若干個(gè)page。如圖5所示得為內(nèi)存中page的結(jié)構(gòu)。drive中的循環(huán)會(huì)在operators之間移動(dòng)page數(shù)據(jù),直到調(diào)度完成或operator無(wú)法繼續(xù)執(zhí)行為止。
2)Shuffles:Presto的設(shè)計(jì)原則旨在最大化資源利用率,同時(shí)最小化端到端延遲,而Presto節(jié)點(diǎn)間的數(shù)據(jù)流機(jī)制正反映了這種設(shè)計(jì)原則。Presto基于HTTP協(xié)議,使用內(nèi)存緩沖區(qū)在節(jié)點(diǎn)間交換中間結(jié)果數(shù)據(jù)來(lái)完成shuffles。tasks產(chǎn)生的數(shù)據(jù)存儲(chǔ)在內(nèi)存緩沖區(qū)中,以供其他worker節(jié)點(diǎn)來(lái)消費(fèi)。Worker間使用HTTP長(zhǎng)輪詢從其他worker節(jié)點(diǎn)請(qǐng)求中間結(jié)果數(shù)據(jù)。Worker節(jié)點(diǎn)上的服務(wù)會(huì)一直保留中間數(shù)據(jù),直到當(dāng)前worker節(jié)點(diǎn)接收到其它worker節(jié)點(diǎn)在請(qǐng)求下一段數(shù)據(jù)時(shí)帶上一次響應(yīng)中的token為止。這樣的確認(rèn)機(jī)制是隱含在傳輸協(xié)議中完成的。長(zhǎng)輪詢機(jī)制最大程度地縮短了響應(yīng)時(shí)間,尤其是在傳輸少量數(shù)據(jù)時(shí)更明顯。相對(duì)于其他系統(tǒng)將shuffle數(shù)據(jù)持久化到磁盤(pán),Presto提供的shuffle機(jī)制帶來(lái)了更低的延遲,這也使Presto對(duì)諸如Developer/Advertiser Analytics這樣延遲敏感型的案列提供了很好的支持。
引擎會(huì)調(diào)整并行度以維持輸出和輸入緩存的目標(biāo)利用率。滿負(fù)荷的使用輸出緩存,會(huì)導(dǎo)致對(duì)split的計(jì)算停止并耗盡寶貴的內(nèi)存資源,而未充分利用的輸入緩存也會(huì)增加不必要的處理開(kāi)銷。
引擎會(huì)持續(xù)監(jiān)控輸出緩存的利用率。當(dāng)利用率居高不下時(shí),它會(huì)減少可被執(zhí)行的split數(shù)量,從而有效的降低并發(fā)度。該措施可以提高網(wǎng)絡(luò)資源共享的公平性。當(dāng)客戶端(用戶端或其它woker)的處理速度跟不上數(shù)據(jù)的產(chǎn)生速度時(shí),這也是一種重要的優(yōu)化措施。如果沒(méi)有這樣的措施,那些處理速度較慢的客戶端在運(yùn)行復(fù)雜的多階段查詢時(shí)會(huì)一直持有數(shù)十GB的緩存。在 Interactive Analytics的案列中,Bi或查詢工具通過(guò)慢速的連接下載少量的數(shù)據(jù)(通常10~50MB)時(shí),這樣的情況會(huì)經(jīng)常出現(xiàn)。
在接收端,引擎會(huì)監(jiān)控每個(gè)請(qǐng)求傳輸?shù)钠骄鶖?shù)據(jù)量來(lái)計(jì)算目標(biāo)HTTP請(qǐng)求的并發(fā)度,這樣可以保證輸入緩存得到充分的利用并且不超出輸入緩存的的容量。這也可以讓上游任務(wù)在輸入緩存填滿時(shí)減慢執(zhí)行速度。
3)寫(xiě):ETL作業(yè)通常會(huì)產(chǎn)生必須寫(xiě)入其他表的數(shù)據(jù)。在遠(yuǎn)程存儲(chǔ)環(huán)境中寫(xiě)性能的重要驅(qū)動(dòng)因素就是寫(xiě)操作的并發(fā)度(如通過(guò)Connector Data Sink API來(lái)控制數(shù)據(jù)寫(xiě)線程的數(shù)量)。
在使用Amazon S3作為存儲(chǔ)的Hive-connector的案例中,對(duì)S3的每次并發(fā)寫(xiě)入都會(huì)創(chuàng)建一個(gè)新文件,而數(shù)百次的小批量數(shù)據(jù)的寫(xiě)則會(huì)產(chǎn)生許多小文件。除非后續(xù)會(huì)合并這些小文件,否則在讀取文件時(shí)會(huì)導(dǎo)致難以接受的高開(kāi)銷(許多慢速的元數(shù)據(jù)操作以及延遲會(huì)限制讀取性能)。但是,使用過(guò)低的并發(fā)會(huì)使聚合寫(xiě)的吞吐量降低到無(wú)法接受的水平。Presto再次采用一種自適應(yīng)的策略,當(dāng)引擎認(rèn)定stage中寫(xiě)操作生成的數(shù)據(jù)超出了緩存利用率的閾值(可配置的閾值)時(shí),會(huì)通過(guò)在更多的Worker節(jié)點(diǎn)上添加任務(wù)來(lái)動(dòng)態(tài)增加寫(xiě)程序的并發(fā)度。在寫(xiě)操作很重的Batch ETL案列中,這是一種重要的優(yōu)化方式。
F. 資源管理
Presto非常適合多租戶使用,而關(guān)鍵的因素就在于它內(nèi)置了一個(gè)細(xì)粒度資源管理系統(tǒng)。這使得一個(gè)集群可以同時(shí)執(zhí)行數(shù)百個(gè)查詢,并最大程度地利用CPU,IO和內(nèi)存資源。
1)CPU調(diào)度:Presto是針對(duì)整個(gè)集群的吞吐率進(jìn)行優(yōu)化的,如整合整個(gè)集群的CPU來(lái)處理數(shù)據(jù)。此外,本地(節(jié)點(diǎn)層面)的調(diào)度器針對(duì)成本較低且低周轉(zhuǎn)時(shí)間的查詢進(jìn)行了優(yōu)化,以及在具有類似CPU需求的查詢之間公平的共享CPU資源。一個(gè)task的CPU使用情況是通過(guò)累加分配給每個(gè)split的處理線程的CPU時(shí)間得來(lái)的。為了最大程度地減少協(xié)調(diào)開(kāi)銷,Presto會(huì)在任務(wù)級(jí)別跟蹤C(jī)PU資源使用情況,并在本地做出調(diào)度決策。
Presto在每個(gè)Worker節(jié)點(diǎn)上調(diào)度許多并發(fā)任務(wù),以實(shí)現(xiàn)多租戶,并且使用了一個(gè)協(xié)作的多任務(wù)處理模型。任意給定的split只能在一個(gè)線程上最多運(yùn)行1秒鐘,之后必須放棄該線程并返回隊(duì)列中。當(dāng)輸出緩存已滿(下游階段無(wú)法足夠快地消耗數(shù)據(jù)),輸入緩存為空(上游階段可能無(wú)法足夠快地生成數(shù)據(jù)),或系統(tǒng)內(nèi)存不足時(shí),本地調(diào)度器會(huì)在當(dāng)前任務(wù)CPU運(yùn)行時(shí)間(指運(yùn)行1秒中)到達(dá)之前切換到另一個(gè)任務(wù)。這樣釋放了那些正在處理split的線程,幫助Presto最大限度的利用CPU,從而適應(yīng)不同的查詢。我們之前介紹到的案列都受益于這種細(xì)粒度的資源管控。
當(dāng)一個(gè)split放棄當(dāng)前的線程時(shí),查詢引擎會(huì)確定一下個(gè)要被執(zhí)行的task(與一個(gè)或多個(gè)split相關(guān)聯(lián))。Presto并沒(méi)有提前預(yù)測(cè)完成一個(gè)新的查詢所需的資源,而只是使用任務(wù)的總CPU時(shí)間,將一個(gè)多級(jí)反饋隊(duì)列劃分成五個(gè)層級(jí)。隨著task累積的CPU時(shí)間越來(lái)越多,它將被劃分到更高的級(jí)別。每個(gè)級(jí)別都指定了一個(gè)可配置的CPU時(shí)間。在實(shí)際應(yīng)用中,要在任意負(fù)載下公平的協(xié)調(diào)多任務(wù)的處理是相當(dāng)有挑戰(zhàn)性的。不同的split使用的IO和CPU的差異性相當(dāng)?shù)拇?,即便在相同的任?wù)中也是如此,而復(fù)雜的函數(shù)(如正則表達(dá)式)相對(duì)于其他的split可能會(huì)消耗更多的線程時(shí)間。而某些Connector不提供異步的API,因此有的工作線程會(huì)被保留數(shù)分鐘。
在處理這些約束條件時(shí),調(diào)度程序必須是自適應(yīng)的。系統(tǒng)提供了低成本的信號(hào)量,因此在一個(gè)operator中可以暫停長(zhǎng)時(shí)間的運(yùn)算。如果operator中的停止時(shí)間超過(guò)特定的值,調(diào)度器會(huì)將實(shí)際的線程時(shí)間計(jì)入當(dāng)前task,并且會(huì)暫時(shí)調(diào)低這個(gè)任務(wù)將來(lái)的執(zhí)行頻率。這種自適應(yīng)的特性允許我們?cè)贗nteractive Analytics 以及Batch ETL的案例中處理不同類型的查詢,其中presto可以為消耗最低資源的查詢提供更高的優(yōu)先級(jí)。我們也可以這樣來(lái)理解,用戶只希望低成本的查詢可以被快速執(zhí)行,而不必?fù)?dān)心高成本的查詢的運(yùn)轉(zhuǎn)時(shí)間。同時(shí)運(yùn)行更多的查詢,即使以犧牲更多上下文切換為代價(jià),也可以減少總的排隊(duì)時(shí)間,因?yàn)榈统杀镜牟樵儠?huì)被快速的執(zhí)行并退出。
2)內(nèi)存管理:在Presto這樣的多租戶系統(tǒng)中,內(nèi)存構(gòu)成了資源管理的主要挑戰(zhàn)之一。這一節(jié)我們會(huì)介紹presto在集群中的內(nèi)存分配機(jī)制。
內(nèi)存池:Presto中的所有的內(nèi)存資源都要被劃分成相應(yīng)內(nèi)存池中的用戶內(nèi)存(user memory)、系統(tǒng)內(nèi)存(systerm memory)以及保留內(nèi)存(reserve memory)。User memory可以理解為系統(tǒng)或輸入數(shù)據(jù)所使用的內(nèi)存(內(nèi)存的使用情況和輸入數(shù)據(jù)的基數(shù)成正比)。而systerm memory則是伴隨著查詢執(zhí)行而產(chǎn)生的(如shuffle緩存等),和查詢類型以及輸入數(shù)據(jù)量沒(méi)有太大的關(guān)系。
Presto對(duì)user memory以及total memory(user+system)是做單獨(dú)限制的,如果查詢超過(guò)了全局的內(nèi)存限制(所有worker節(jié)點(diǎn)使用的內(nèi)存的總和)或者超過(guò)了單個(gè)worker的限制,查詢都會(huì)被kill掉。當(dāng)Worker內(nèi)存不足時(shí),presto會(huì)通過(guò)停止tasks來(lái)阻塞內(nèi)存分配。Total memory的限制通常設(shè)置得比user memory限制大得多,在生產(chǎn)環(huán)境中只有少部分的查詢會(huì)超過(guò)total memory的限制。
對(duì)于一個(gè)查詢而言,單worker設(shè)置的user memory限制和全局的user memory限制通常是不同的;這樣可以最大限度的允許內(nèi)存使用的傾斜。比如,有一個(gè)500個(gè)節(jié)點(diǎn)的集群,每個(gè)節(jié)點(diǎn)有100GB的user memory,這樣每個(gè)查詢?cè)诩褐凶疃嗫梢允褂枚噙_(dá)5T的內(nèi)存。在這種情況下,將總的內(nèi)存分配給10個(gè)并發(fā)執(zhí)行的查詢。但是,如果我們?cè)试S2:1的傾斜(即一個(gè)查詢子部分的內(nèi)存消耗是內(nèi)存消耗中位數(shù)的兩倍),那么每個(gè)Worker節(jié)點(diǎn)上的查詢內(nèi)存限制需要被設(shè)置成20GB。這意味著在不耗盡可用節(jié)點(diǎn)內(nèi)存的情況下,僅可以保證5個(gè)查詢能夠運(yùn)行。(譯者注:一個(gè)Worker100G,分給10個(gè)查詢那么每個(gè)查詢分到10G,如果允許2:1的傾斜,那么單個(gè)查詢的內(nèi)存需要限制20G,那么對(duì)于100G的Worker來(lái)說(shuō)最多只能運(yùn)行5個(gè)查詢)
但值得注意的是,在InteractiveAnalytics和BatchETL的案列中,我們能夠在500個(gè)節(jié)點(diǎn)的集群上運(yùn)行遠(yuǎn)超過(guò)5個(gè)的查詢。鑒于這些集群中的查詢?cè)谄鋬?nèi)存使用特征(傾斜,分配速率和分配的時(shí)間點(diǎn))方面存在巨大差異,因此這5個(gè)查詢?cè)谌魏螘r(shí)間點(diǎn)都不可能在一個(gè)Worker上使用到最大限制的內(nèi)存。因此,當(dāng)Worker節(jié)點(diǎn)內(nèi)存不足時(shí),只要有相應(yīng)的機(jī)制能夠保證集群的健康,那么就可以放心的過(guò)量的使用內(nèi)存。Presto提供了兩種保障機(jī)制:溢出(spilling)和預(yù)留池(reserved pool)。
譯者注:0.217中system memory pool被移除了
Spilling:當(dāng)Worker節(jié)點(diǎn)內(nèi)存不足時(shí),presto會(huì)對(duì)滿足條件的task按照?qǐng)?zhí)行時(shí)間進(jìn)行升序排序,并對(duì)這些task進(jìn)行內(nèi)存回收。當(dāng)有足夠的內(nèi)存滿足最后一次請(qǐng)求調(diào)用時(shí),則停止內(nèi)存回收。內(nèi)存回收是通過(guò)將內(nèi)存溢出到磁盤(pán)來(lái)完成的。Presto支持對(duì)哈希連接以及聚合操作的溢出。但是在Facebook的部署環(huán)境中,我們未開(kāi)啟溢出功能。因?yàn)榧旱姆植际絻?nèi)存足夠的大,通常是好幾TB,用戶通常對(duì)預(yù)期的查詢延遲感到滿意,而且溢出到本地磁盤(pán)會(huì)增加硬件成本的開(kāi)銷(尤其是在Facebook的共享存儲(chǔ)的部署模式下)。
Reserved Pool:如果節(jié)點(diǎn)內(nèi)存不足,并且集群沒(méi)有配置為Spilling,或沒(méi)有剩余的可撤銷內(nèi)存,reserved pool機(jī)制可以保證集群不被阻塞。每個(gè)節(jié)點(diǎn)上的查詢內(nèi)存池會(huì)進(jìn)一步被細(xì)分為兩個(gè)池:general pool和reserved pool。當(dāng)Worker節(jié)點(diǎn)上的general pool耗盡時(shí),那么在worker節(jié)點(diǎn)上的占用內(nèi)存最多的那查詢會(huì)在整個(gè)集群中被提升到reserved pool中。在這種情況下,該查詢所消耗的是reserved pool中的內(nèi)存,而不在是general pool中的內(nèi)存。為了避免死鎖,在整個(gè)集群中同時(shí)只有一個(gè)查詢可以在reserved pool中執(zhí)行。如果Worker節(jié)點(diǎn)上的general pool內(nèi)存已用完,而reserved pool也已經(jīng)被占用,那么該Worker節(jié)點(diǎn)上其他task的所有內(nèi)存相關(guān)的請(qǐng)求將被阻塞。運(yùn)行在reserved pool中的查詢會(huì)一直占用該pool直到其執(zhí)行完成,這個(gè)時(shí)候,群集將停止阻塞之前所有未完成的內(nèi)存請(qǐng)求。這在某種程度上看起來(lái)有點(diǎn)浪費(fèi),因此必須合理的調(diào)整每個(gè)Worker節(jié)點(diǎn)上reserved pool的大小,以滿足在本地內(nèi)存限制下運(yùn)行查詢。當(dāng)某個(gè)查詢阻塞了大部分的Worker節(jié)點(diǎn)時(shí),集群也可以配置成kill掉這個(gè)查詢。
G. 容錯(cuò)
目前,Presto可以通過(guò)低級(jí)別的重試從臨時(shí)的錯(cuò)誤中恢復(fù)。但是,截至2018年末,Presto對(duì)Coordinator或Worker節(jié)點(diǎn)的崩潰,沒(méi)有任何內(nèi)置的有意義的容災(zāi)措施。Coordinator的故障將導(dǎo)致集群的不可用,而Worker節(jié)點(diǎn)的崩潰將導(dǎo)致該節(jié)點(diǎn)上所有正在執(zhí)行的查詢失敗。目前,對(duì)于這樣的錯(cuò)誤,Presto需要依靠Client端重新提交失敗的查詢來(lái)解決。
目前,在Facebook的生產(chǎn)環(huán)境中是通過(guò)額外的機(jī)制保證某些特定場(chǎng)景下的高可用。在Interactive Analytics 和Batch ETL 的案例中運(yùn)行著一個(gè)備用的Coordinator,而在A/B Testing以及Developer/Advertiser Analytics 的案列中,運(yùn)行著多個(gè)集群來(lái)保證高可用。外部監(jiān)控系統(tǒng)會(huì)識(shí)別那些產(chǎn)生異常故障數(shù)量的節(jié)點(diǎn)并將它們從集群中剔除,而被修復(fù)的節(jié)點(diǎn)會(huì)重新加入集群。以上的措施都是在某種程度上降低服務(wù)不可用的時(shí)間,但是無(wú)法完全屏蔽故障的發(fā)生。
常用的check pointing 以及部分恢復(fù)機(jī)制通常是非常消耗計(jì)算資源的,并且很難在即席查詢系統(tǒng)中實(shí)現(xiàn)?;趶?fù)制策略的容錯(cuò)機(jī)制通常也是相當(dāng)耗資源的??紤]到這樣的成本開(kāi)銷,這種技術(shù)的預(yù)期價(jià)值還尚不明確,尤其是在考慮到節(jié)點(diǎn)平均故障時(shí)間時(shí),如在Batch ETL的案列中,集群的節(jié)點(diǎn)數(shù)達(dá)到了數(shù)千個(gè)并且大部分的查詢都是在數(shù)小時(shí)之內(nèi)完成的。在其他的研究中也得出過(guò)類似的結(jié)論。
但是,我們正在積極致力于提高長(zhǎng)時(shí)間運(yùn)行的查詢的容錯(cuò)能力。我們正在評(píng)估添加可選的check pointing以及重試那些無(wú)法以流水線方式運(yùn)行的計(jì)劃子樹(shù)。
Ⅴ. 查詢過(guò)程優(yōu)化
A. 使用JVM
Presto是通過(guò)Java語(yǔ)言實(shí)現(xiàn)的,并且運(yùn)行在Hotspot Java虛擬機(jī)上。為了在Presto的實(shí)現(xiàn)中獲得最佳的執(zhí)行性能,需要發(fā)揮基礎(chǔ)平臺(tái)的優(yōu)勢(shì)并且避免其劣勢(shì)。性能敏感型的代碼(如數(shù)據(jù)壓縮或校驗(yàn)和算法)可以受益于特定的優(yōu)化或CPU指令。盡管沒(méi)有應(yīng)用級(jí)的機(jī)制來(lái)控制JVM即時(shí)編譯器(JIT)如何生成機(jī)器碼,但是可以對(duì)代碼進(jìn)行結(jié)構(gòu)化,以便其可以利用JIT編譯器提供的優(yōu)化方式,如方法內(nèi)聯(lián),循環(huán)體展開(kāi)以及內(nèi)聯(lián)函數(shù)。我們正在探索當(dāng)JVM無(wú)法生成最佳機(jī)器碼的情況下(如128位的數(shù)學(xué)運(yùn)算)使用Graal。
垃圾收集(GC)算法的選擇會(huì)對(duì)應(yīng)用程序性能產(chǎn)生巨大的影響,甚至可能影響應(yīng)用程序的實(shí)現(xiàn)方式。Presto使用G1收集器,該收集器在處理大于特定大小的對(duì)象時(shí)性能較差。為了限制這類對(duì)象的數(shù)量,Presto避免分配大于“humongous”閾值的對(duì)象或緩存,并在必要時(shí)使用分段數(shù)組。由于G1需要維護(hù)對(duì)象集合的結(jié)構(gòu)(remembered sets tructures),所以大型和高度關(guān)聯(lián)的對(duì)象圖可能會(huì)存在一些問(wèn)題。查詢執(zhí)行路徑上的關(guān)鍵步驟的數(shù)據(jù)結(jié)構(gòu)是通過(guò)扁平化得內(nèi)存數(shù)組來(lái)實(shí)現(xiàn)的,目的就是為了減少引用以及對(duì)象數(shù)量,從而使垃圾回收變得相對(duì)輕松一點(diǎn)。例如,在HISTOGRAM聚合中,會(huì)在一個(gè)扁平的數(shù)組或哈希表中存儲(chǔ)所有組中的桶鍵(bucket keys)以及對(duì)象計(jì)數(shù),而不是為每一個(gè)histogram維護(hù)獨(dú)立的對(duì)象。
B. 代碼生成
引擎的主要性能特征之一,就是生產(chǎn)JVM字節(jié)碼。這有如下兩種表現(xiàn)形式:
1)表達(dá)式求值:查詢引擎的性能部分取決于它對(duì)復(fù)雜表達(dá)式的求值速度。Presto提供了一個(gè)表達(dá)式解釋器,該表達(dá)式解釋器在我們的測(cè)試場(chǎng)景中可以對(duì)任意復(fù)雜的表達(dá)式進(jìn)行求值,但對(duì)于數(shù)十億行的生產(chǎn)數(shù)據(jù)的求值還是太慢了。為了加速表達(dá)式求值,Presto生成字節(jié)碼來(lái)處理常量、函數(shù)調(diào)用、對(duì)變量的引用以及延遲或存在短路效應(yīng)的操作。
2)針對(duì)JIT優(yōu)化器:Presto會(huì)為一些關(guān)鍵算子(operators)和算子組合生成字節(jié)碼。字節(jié)碼生成器利用引擎在計(jì)算語(yǔ)義方面的優(yōu)勢(shì)來(lái)生成更易于JIT優(yōu)化器進(jìn)行優(yōu)化的字節(jié)碼。生成器主要會(huì)針對(duì)如下三種行為:
由于引擎會(huì)在不同任務(wù)管道(task pipeline)的不同split之間進(jìn)行切換,因此JIT無(wú)法優(yōu)化那些基于常見(jiàn)的循環(huán)的實(shí)現(xiàn)。這是因?yàn)椋诓樵兊倪^(guò)程中從循環(huán)中得到的分析信息可能會(huì)被其他的task或查詢所破壞。
即使在單個(gè)task pipeline中的循環(huán)處理,引擎也知道每次計(jì)算所涉及的類型,并且可以在列上生成展開(kāi)的循環(huán)。消除循環(huán)體中的類型差異會(huì)讓分析器得出如下的結(jié)論:?jiǎn)我徽{(diào)用可以對(duì)虛擬方法進(jìn)行內(nèi)聯(lián)。
由于為每個(gè)任務(wù)生成的字節(jié)碼被編譯到一個(gè)單獨(dú)的Java類中,因此JIT優(yōu)化器可以分別對(duì)每一個(gè)字節(jié)碼進(jìn)行概要分析。實(shí)際上, JIT優(yōu)化器會(huì)為處理實(shí)際數(shù)據(jù)的代碼做進(jìn)一步的調(diào)整。這種基于分析結(jié)果的調(diào)整可以在每一個(gè)獨(dú)立的task中展開(kāi),從而提高了每個(gè)task在處理數(shù)據(jù)的不同分區(qū)時(shí)的性能。此外,性能分析可以在任務(wù)的整個(gè)生命周期中隨著數(shù)據(jù)(如,時(shí)間序列數(shù)據(jù)或日志數(shù)據(jù))的變化而變化,從而使生成的代碼也隨之變化。
生成的字節(jié)碼還得益于內(nèi)聯(lián)所帶來(lái)的二次效應(yīng)。JVM能夠擴(kuò)大優(yōu)化范圍,自動(dòng)向量化大部分的計(jì)算,并且可以利用基于頻率的基本塊布局來(lái)最大程度地減少分支。這樣使得CPU分支預(yù)測(cè)也變得更加有效。字節(jié)碼生成提高了引擎將中間結(jié)果存儲(chǔ)在CPU寄存器或CPU緩存中而不是內(nèi)存中的能力。
C. 文件格式的特性
掃描算子(Scan operator)使用葉子階段split的信息來(lái)調(diào)用Connector API,并以Pages的形式接收列數(shù)據(jù)。一個(gè)page由一個(gè)block列表組成,而每個(gè)block是具有扁平內(nèi)存表示形式的列。使用扁平內(nèi)存的數(shù)據(jù)結(jié)構(gòu)對(duì)性能非常重要,尤其是對(duì)那些復(fù)雜的數(shù)據(jù)類型。在緊湊的循環(huán)體內(nèi),指針跟蹤,拆箱和虛擬方法調(diào)用都增加了大量的開(kāi)銷。
Hive和Raptor這類connectors會(huì)盡可能的使用特定的文件格式。Presto配有自定義的reader,可以通過(guò)使用文件頁(yè)眉/頁(yè)腳中的統(tǒng)計(jì)信息(如頁(yè)眉中保存的最小-最大范圍和布隆過(guò)濾器),來(lái)高效地過(guò)濾數(shù)據(jù)。Reader可以直接將某些形式的壓縮數(shù)據(jù)直接讀成blocks,從而讓引擎可以有效地對(duì)其進(jìn)行處理。
圖5顯示了一個(gè)page中列的布局方式,其中每一列都有其對(duì)應(yīng)的編碼方式。字典編碼的塊(DictionaryBlock)在壓縮數(shù)據(jù)的低基數(shù)部分非常高效,游程編碼的塊(run-length encoded block,RLEBlock)對(duì)重復(fù)的數(shù)據(jù)進(jìn)行壓縮。多個(gè)page可以共享一個(gè)字典,這可以大大提高內(nèi)存利用率。ORC文件中的一列可以為整個(gè)“stripe”(最多數(shù)百萬(wàn)行),使用單個(gè)字典。

D.數(shù)據(jù)的懶加載
Presto支持?jǐn)?shù)據(jù)的惰性物化(lazy materialization)。此功能可以利用ORC,Parquet和RCFile等文件格式的列壓縮特性。Connector可以生成惰性的blocks,僅當(dāng)實(shí)際訪問(wèn)時(shí)才讀取,解壓縮和解碼數(shù)據(jù)。鑒于大部分的CPU時(shí)間都花在了解壓縮和解碼上,并且查詢的數(shù)據(jù)都是有目的性的,因此這種優(yōu)化在不經(jīng)常訪問(wèn)列的情況下非常有效。在Batch ETL的案列中,對(duì)生產(chǎn)環(huán)境中負(fù)載樣本進(jìn)行的測(cè)試表明,懶加載將提取的數(shù)據(jù)減少了78%,將單元加載的數(shù)據(jù)減少了22%,將總CPU時(shí)間減少了14%。
E. 操作壓縮數(shù)據(jù)
Presto會(huì)盡可能的處理來(lái)自Connector的壓縮數(shù)據(jù)(即Dictionary和RLE block)。圖5中顯示了page內(nèi)這些block的結(jié)構(gòu)。當(dāng)page處理器在計(jì)算轉(zhuǎn)換或過(guò)濾時(shí)遇到dictionary block,它將處理 dictionary 內(nèi)的所有的值(或RLE的塊中的單個(gè)值)。這允許引擎以快速的無(wú)條件循環(huán)處理整個(gè)dictionary。在某些情況下,dictionary中存在的值多于block中的行。在這種場(chǎng)景下,page處理器推測(cè)未引用的值將在后續(xù)的block中使用。Page處理器會(huì)持續(xù)跟蹤產(chǎn)生的實(shí)際行數(shù)和dictionary的大小,這有利于對(duì)比處理索引和處理dictionary的效率。如果行數(shù)大于dictionary的大小,則處理dictionary的效率可能更高。當(dāng)page處理器在block序列中遇到了新的dictionary時(shí),它會(huì)使用這種啟發(fā)式的方法確定是否繼續(xù)推測(cè)。
在構(gòu)建哈希表(如聯(lián)接或聚合)時(shí),Presto還會(huì)利用dictionary block結(jié)構(gòu)。在處理索引時(shí),operator會(huì)在數(shù)組中記錄每個(gè)dictionary 條目(entry)在哈希表中的位置。如果有條目在后續(xù)的索引中重復(fù),他會(huì)簡(jiǎn)單的重用該條目的的位置而不是重新對(duì)他進(jìn)行計(jì)算。當(dāng)連續(xù)的blocks共享同一dictionary 時(shí),page處理器將保留該數(shù)組,來(lái)進(jìn)一步減少必要的計(jì)算。
Presto在執(zhí)行期間還會(huì)產(chǎn)生中間壓縮的結(jié)果。例如,連接處理器(join processor)會(huì)在效率更高的情況下生成dictionary或RLE block。對(duì)于哈希聯(lián)接,當(dāng)聯(lián)接的查找側(cè)在哈希表中查找鍵時(shí),它將值索引記錄到數(shù)組中,而不是復(fù)制實(shí)際數(shù)據(jù)。Operator只是簡(jiǎn)單地生成了一個(gè)dictionary block,其中索引列表是該數(shù)組,而dictionary是對(duì)哈希表中該block的引用
Ⅵ. 性能
在這一部分,我們提供能了性能測(cè)試的結(jié)果,展現(xiàn)了本文所描述的設(shè)計(jì)決策對(duì)性能的影響。
A. 適應(yīng)性
在Facebook內(nèi),我們?cè)谏a(chǎn)環(huán)境中運(yùn)行著幾種不同的connectors,來(lái)允許用戶處理存儲(chǔ)在各種內(nèi)部系統(tǒng)中的數(shù)據(jù)。 表1描述了第II節(jié)中提到的幾種案例的中的connector和部署方式。為了演示Presto是如何適配connector的特性,我們?cè)?0TB的數(shù)據(jù)上進(jìn)行TPC-DS基準(zhǔn)測(cè)試來(lái)對(duì)比查詢時(shí)間。盡管Presto能夠運(yùn)行所有的TPS-DS查詢,但是為了本次實(shí)驗(yàn),我們選擇了那些不需要溢出設(shè)置的查詢子集。

我們使用的是0.211版本的presto以及內(nèi)部使用的類似于Hive/HDFS 和 Raptor的connector。Raptor是專為Presto設(shè)計(jì)的無(wú)共享存儲(chǔ)引擎。它使用MySQL來(lái)存儲(chǔ)元數(shù)據(jù),并以O(shè)RC格式將數(shù)據(jù)存儲(chǔ)在本地閃存盤(pán)上。Raptor支持復(fù)雜的數(shù)據(jù)組織方式(排序、分桶和臨時(shí)列),但是對(duì)于本實(shí)驗(yàn),我們的數(shù)據(jù)是隨機(jī)分區(qū)的。Hive connector使用類似于Hive Metastore的內(nèi)部服務(wù),并在功能與HDFS相似的遠(yuǎn)程分布式文件系統(tǒng)上訪問(wèn)類似于ORC格式的文件。
每一個(gè)查詢都會(huì)在如下三種配置的測(cè)試集群上運(yùn)行:(1)Raptor中的數(shù)據(jù)存儲(chǔ)在節(jié)點(diǎn)之間隨機(jī)分布的表分片中。(2)Hive/HDFS中存儲(chǔ)的數(shù)據(jù),無(wú)統(tǒng)計(jì)信息。(3)Hive/HDFS中存儲(chǔ)的數(shù)據(jù)帶有表和列的統(tǒng)計(jì)信息。只要有這些統(tǒng)計(jì)信息,Presto優(yōu)化器就可以根據(jù)成本決定連接順序和連接策略。每個(gè)節(jié)點(diǎn)的配置為28核2.4GHz的Intel Xeon E5-2680 v4 CPU,1.6T閃存盤(pán)和256GB DDR4 RAM。
圖6顯示Presto在查詢運(yùn)行時(shí)受到connector特性的影響非常大。在不更改查詢或群集配置的情況下,Presto能夠通過(guò)利用connector的特性(包括吞吐量,延遲和可用的統(tǒng)計(jì)信息)來(lái)適應(yīng)當(dāng)前的connector。這也證明了Presto既可以作為傳統(tǒng)的企業(yè)數(shù)倉(cāng)也可以作為Hadoop數(shù)倉(cāng)的查詢引擎。Facebook的數(shù)據(jù)工程師經(jīng)常會(huì)使用Presto對(duì)Hadoop數(shù)倉(cāng)上的數(shù)據(jù)進(jìn)行探索性分析,然后將匯總結(jié)果和經(jīng)常訪問(wèn)的數(shù)據(jù)加載到Raptor中,以進(jìn)行更快的分析和低延遲的儀表盤(pán)展示。

B. 靈活性
Presto的靈活性很大程度上歸功于其低延遲的數(shù)據(jù)shuffle機(jī)制,以及支持對(duì)大量數(shù)據(jù)進(jìn)行高性能處理的Connector API。圖7顯示了之前介紹的案例在我們生產(chǎn)環(huán)境中的查詢運(yùn)行時(shí)間的分布。我們只選取了那些執(zhí)行成功且發(fā)生數(shù)據(jù)讀取的查詢。結(jié)果表明,Presto可以適用于具有嚴(yán)格延遲限制(20~100ms)的web服務(wù),同樣也適用于那些需要運(yùn)行數(shù)小時(shí)的ETL作業(yè)。

C. 資源管理
Presto集成了細(xì)粒度的資源管理系統(tǒng),使它能夠在不同的查詢之間快速周轉(zhuǎn)CPU和內(nèi)存資源,以最大程度地提高在多租戶群集中的資源利用效率。圖8顯示了來(lái)自Interactive Analytics案例集群的CPU和并發(fā)指標(biāo)的4個(gè)小時(shí)的跟蹤記錄。即使從44個(gè)查詢的峰值下降到8個(gè)查詢的低點(diǎn),Presto仍繼續(xù)在每個(gè)Worker節(jié)點(diǎn)上平均使用約90%的CPU。同樣值得注意的是,即便如此調(diào)度器仍然可以對(duì)那些低負(fù)載的查詢進(jìn)行優(yōu)先執(zhí)行并保持響應(yīng)。一旦接收了新的查詢,它會(huì)在毫秒級(jí)的時(shí)間內(nèi)給這些查詢分配大量的集群CPU資源。

Ⅶ. 工程相關(guān)
自2013年以來(lái),Presto由Facebook的一個(gè)小團(tuán)隊(duì)開(kāi)發(fā),并對(duì)外提供服務(wù)。我們注意到,在快速發(fā)展的環(huán)境中,一些工程哲學(xué)對(duì)Presto的設(shè)計(jì)產(chǎn)生了巨大影響:
基于配置的自適應(yīng)性:作為執(zhí)行用戶任意定義的計(jì)算的復(fù)雜的多租戶查詢引擎,Presto必須適應(yīng)于不同的查詢,還需要適有各種組合的特征。例如,在Presto能夠自適應(yīng)端到端的讀寫(xiě)壓力之前,部分具有慢客戶端的作業(yè)會(huì)占用大量的內(nèi)存和CPU資源,這會(huì)對(duì)同時(shí)正在運(yùn)行的延遲敏感性的作業(yè)產(chǎn)生不利的影響。如果Presto不具備自適應(yīng)性,則必須對(duì)工作負(fù)載進(jìn)行分區(qū),并分別調(diào)整每個(gè)工作負(fù)載的配置。但是這種方式無(wú)法擴(kuò)展大我們生產(chǎn)環(huán)境中的各種查詢用例中。
唾手可得的工具:Presto在查詢(query)和節(jié)點(diǎn)(work node)層面提供了細(xì)粒度的性能統(tǒng)計(jì)信息。我們維護(hù)著自己的庫(kù)(libraries),以便高效低地收集統(tǒng)計(jì)信息,而對(duì)于近似的數(shù)據(jù)結(jié)構(gòu)我們使用扁平的內(nèi)存進(jìn)行存儲(chǔ)。鼓勵(lì)那些可被觀察的系統(tǒng)設(shè)計(jì)并且允許工程師們檢測(cè)和理解其代碼的性能是非常重要的。我們的庫(kù)使添加統(tǒng)計(jì)信息變得與注釋方法一樣容易。并且我們針對(duì)每一個(gè)查詢收集了算子(operator)級(jí)別的統(tǒng)計(jì)信息,并將這些信息合并到task以及stage級(jí)別的統(tǒng)計(jì)信息中。我們?cè)谶@監(jiān)控上的投入,也使我們能夠在優(yōu)化系統(tǒng)時(shí)以數(shù)據(jù)為驅(qū)動(dòng)。
靜態(tài)配置:在像Presto這樣的復(fù)雜系統(tǒng)中,操作相關(guān)的問(wèn)題很難快速地找到根源并得到解決。屬性配置會(huì)以難以理解的方式影響著系統(tǒng)的性能,因此我們優(yōu)先考慮能夠了解群集狀態(tài),而不是能夠快速更改的配置。
與Facebook的其他幾個(gè)系統(tǒng)不同,Presto盡可能使用靜態(tài)配置而不是動(dòng)態(tài)配置。我們開(kāi)發(fā)了自己的配置庫(kù),它被設(shè)計(jì)成在有任何警告的情況下,在啟動(dòng)時(shí)就會(huì)崩潰;這包括那些未使用,重復(fù)或沖突的屬性。這種模式也面臨著一系列的挑戰(zhàn)。然而,對(duì)于大量的集群和配置項(xiàng),將復(fù)雜性從操作上轉(zhuǎn)移到部署過(guò)程或部署工具上會(huì)更有效。
垂直集成:與其他工程團(tuán)隊(duì)一樣,我們?yōu)樾阅芎托实确浅V匾慕M件設(shè)計(jì)了定制庫(kù)。例如,自定義文件格式讀取器允許我們端到端使用Presto原生的數(shù)據(jù)結(jié)構(gòu),避免了轉(zhuǎn)換開(kāi)銷。然而,我們注意到,當(dāng)操作一個(gè)多線程系統(tǒng)并在一個(gè)長(zhǎng)生命周期的進(jìn)程中執(zhí)行任意計(jì)算時(shí),輕松的調(diào)試和控制庫(kù)行為的能力同樣重要。
考慮一個(gè)近期生產(chǎn)問(wèn)題的例子。Presto使用了Java內(nèi)置的gzip庫(kù)。在調(diào)試一系列的導(dǎo)致系統(tǒng)奔潰的操作時(shí),我們發(fā)現(xiàn)glibc和gzip庫(kù)(調(diào)用本地代碼)之間的交互會(huì)導(dǎo)致內(nèi)存碎片。 對(duì)于特定的工作負(fù)載組合,這會(huì)導(dǎo)致大量的內(nèi)存泄漏。為了解決這個(gè)問(wèn)題,我們改變了使用庫(kù)的方式來(lái)影響正確的緩存刷新方式,但是在其他情況下,我們甚至為壓縮格式編寫(xiě)了自己的庫(kù)。
定制庫(kù)還可以提高開(kāi)發(fā)人員效率:僅實(shí)現(xiàn)必要的功能,統(tǒng)一配置管理并支持詳細(xì)的工具以匹配我們的用例,從而減少了bug的產(chǎn)生。
Ⅷ. 相關(guān)工作
在過(guò)去十年中,針對(duì)大型數(shù)據(jù)集運(yùn)行SQL的系統(tǒng)變得非常流行。而每一個(gè)系統(tǒng)都做了獨(dú)特的取舍。對(duì)這些系統(tǒng)做全面的研究并不在本文的討論范圍之內(nèi)。相反,我們將重點(diǎn)放在該領(lǐng)域的一些更值得注意的工作上。
Apache Hive最初是由Facebook開(kāi)發(fā)的,為存儲(chǔ)在HDFS中的數(shù)據(jù)提供一個(gè)類似sql的接口,并通過(guò)將它們編譯成MapReduce或Tez作業(yè)來(lái)執(zhí)行查詢。Spark SQL是一個(gè)構(gòu)建在流行的Spark引擎上的更現(xiàn)代的系統(tǒng),它解決了MapReduce的許多限制。dSpark SQL可以在多個(gè)分布式數(shù)據(jù)存儲(chǔ)上運(yùn)行大型的查詢,并且可以對(duì)內(nèi)存中的中間結(jié)果進(jìn)行操作。但是,這些系統(tǒng)不支持端到端管道,并且通常在stage間shuffle時(shí)將數(shù)據(jù)持久化到文件系統(tǒng)。雖然這提高了容錯(cuò)性,但是額外的延遲導(dǎo)致了這些系統(tǒng)不適合那些交互式或低延遲的用例。
Vertica ,Teradata,Redshift和Oracle Exadata等產(chǎn)品可以在不同程度上讀取外部數(shù)據(jù)。但是,它們是基于內(nèi)部的數(shù)據(jù)存儲(chǔ)而構(gòu)建的,當(dāng)操作那些加載到系統(tǒng)內(nèi)的數(shù)據(jù)時(shí),能夠達(dá)到最佳性能。一些系統(tǒng)采用了集成RDBMS風(fēng)格和MapReduce執(zhí)行的混合方式,如Microsoft SQL Server Polybase (用于非結(jié)構(gòu)化數(shù)據(jù))和Hadapt(為了性能)。Apache Impala可以提供交互式的延遲,但只能在Hadoop生態(tài)系統(tǒng)中運(yùn)行。相反,Presto則與數(shù)據(jù)源無(wú)關(guān)。管理員可以使用垂直集成的數(shù)據(jù)存儲(chǔ)(如Raptor)來(lái)部署Presto,但也可以配置Presto從各種系統(tǒng)查詢數(shù)據(jù)(包括傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)以及NOSQL數(shù)據(jù)庫(kù))。Presto專有的內(nèi)部服務(wù)和流處理系統(tǒng),使得其在單個(gè)集群中也具有較低的資源開(kāi)銷。
系統(tǒng)和數(shù)據(jù)庫(kù)社區(qū)在開(kāi)發(fā)創(chuàng)新技術(shù)方面有著豐富歷史,而Presto是在這些基礎(chǔ)上開(kāi)發(fā)的。它使用了類似于Neumann和Diaconu等人描述的技術(shù),編制查詢計(jì)劃來(lái)加快查詢處理速度。它可以使用Abadi等人的技術(shù)對(duì)壓縮數(shù)據(jù)進(jìn)行操作,并產(chǎn)生壓縮的中間結(jié)果。可以從多個(gè)投影(projections )中選擇最理想的布局,并且使用了Zhou等類似的策略。通過(guò)推理計(jì)劃屬性來(lái)最大程度地減少shuffle。
Ⅸ. 總結(jié)
在本文中,我們介紹了Presto,這是一種facebook開(kāi)發(fā)的開(kāi)源的MPP SQL查詢引擎,他可以快速的處理大型的數(shù)據(jù)集。Presto被設(shè)計(jì)成非常的靈活,可以將其配置成適用于各種用例的高性能的查詢引擎。其豐富的插件接口和Connector API 使它具有很強(qiáng)的擴(kuò)展性,從而可以和各種環(huán)境中的不同數(shù)據(jù)相結(jié)合。Presto同樣被設(shè)計(jì)成自適應(yīng)的,它可以利用connector提供的功能來(lái)加速執(zhí)行,并可以自動(dòng)調(diào)整讀寫(xiě)并行度,網(wǎng)絡(luò)IO,算子的啟發(fā)方式并根據(jù)系統(tǒng)中運(yùn)行的查詢的特性進(jìn)行調(diào)度。Presto的架構(gòu)使其可以為低延遲的負(fù)載提供服務(wù),并且能夠有效的處理昂貴且長(zhǎng)時(shí)間的查詢。
Presto允許像Facebook這樣的公司通過(guò)部署單個(gè)SQL系統(tǒng)處理多種常見(jiàn)的分析用例,來(lái)查詢多個(gè)存儲(chǔ)系統(tǒng)的數(shù)據(jù),同時(shí)還支持?jǐn)U展到上千個(gè)節(jié)點(diǎn)。它的架構(gòu)設(shè)計(jì)使它能夠在眾多的大數(shù)據(jù)SQL引擎中占有一席之地。在Facebook以及整個(gè)行業(yè)中,Presto的使用率正在快速增長(zhǎng),并且我們的開(kāi)源社區(qū)還在持續(xù)貢獻(xiàn)著。