天天看點

Hologres揭秘:深度解析高效率分布式查詢引擎

Hologres(中文名互動式分析)是阿裡雲自研的一站式實時數倉,這個雲原生系統融合了實時服務和分析大資料的場景,全面相容PostgreSQL協定并與大資料生态無縫打通,能用同一套資料架構同時支援實時寫入實時查詢以及實時離線聯邦分析。它的出現簡化了業務的架構,與此同時為業務提供實時決策的能力,讓大資料發揮出更大的商業價值。從阿裡集團誕生到雲上商業化,随着業務的發展和技術的演進,Hologres也在持續不斷優化核心技術競争力,為了讓大家更加了解Hologres,我們計劃持續推出Hologers底層技術原理揭秘系列,從高性能存儲引擎到高效率查詢引擎,高吞吐寫入到高QPS查詢等,全方位解讀Hologers,請大家持續關注!

往期精彩内容:

本期我們将帶來Hologers高效率分布式查詢引擎的技術原了解析。

Hologres作為HSAP服務分析一體化的落地最佳實踐,其查詢引擎是一個完全自研的執行引擎,它的核心設計目标是支援所有類型的分布式分析和服務查詢,并做到極緻查詢性能。為了做到這一點,我們借鑒了各種分布式查詢系統,包括分析型資料庫,實時數倉等,吸取了各方面的優勢從零開始打造出一個全新的執行引擎。

為什麼要選擇從零開始做一個新的查詢引擎?開源的分布式分析查詢系統主要有兩大類:

  • 一類是傳統的 Massively Parallel Processing 系統,能夠支援通用的 SQL 查詢,但是對實時場景支援不夠好,性能不夠理想。
  • 一類是 Apache Druid 和 ClickHouse這些實時數倉,是專門為實時場景設計和優化的,能夠比較好地支援一些常見的單表實時查詢,但是複雜查詢的性能比較差。
  • 另外大資料生态圈基于 MapReduce 的引擎比較适合批處理 ETL,一般不太适合線上服務和多元分析的場景,性能也差不少。

Hologres 執行引擎是在一個能支援複雜查詢和上述高性能實時服務查詢的通用架構,先首先實作了常用的實時數倉場景,深入優化并用内部 Benchmark 驗證了性能和穩定性超過包括專用實時數倉的其它競品之後,再擴充到其它複雜查詢的支援。擴充的過程中,在不可避免地系統變得越來越複雜的同時,也用 Benchmark 幫助保持簡單實時查詢的性能沒有回退。如果在已有的查詢引擎上做改進,因為很多架構和設計上的選擇已經定型,牽一發而動全身,就很難達到這樣的效果。

Hologres執行引擎從開發到落地實踐面臨了非常多的挑戰,但也給我們提供了機會把這個領域的各種新進展都結合利用起來,并超越已有系統做到對各種查詢類型的高性能處理,其背後主要是基于以下特點:

  • 分布式執行模型:一個和存儲計算分離架構配合的分布式執行模型。執行計劃由異步算子組成的執行圖 DAG(有向無環圖) 表示,可以表達各種複雜查詢,并且完美适配 Hologres 的資料存儲模型,友善對接查詢優化器,利用業界各種查詢優化技術。
  • 全異步執行:端到端的全異步處理架構,可以避免高并發系統的瓶頸,充分利用資源,并且最大可能地避免存儲計算分離系統帶來的讀資料延遲的影響。
  • 向量化和列處理:算子内部處理資料時最大可能地使用向量化執行,和存儲引擎的深度內建,通過靈活的執行模型,充分利用各種索引,并且最大化地延遲向量物化和延遲計算,避免不必要的讀資料和計算。
  • 自适應增量處理:對常見實時資料應用查詢模式的自适應增量處理。
  • 特定查詢深度優化:對一些查詢模式的獨特優化

下面将會對各個子產品一一介紹。

分布式執行模型

Hologres 是能夠彈性無限水準擴充資料量和計算能力的系統,需要能夠支援高效的分布式查詢。

Hologres 查詢引擎執行的是由優化器生成的分布式執行計劃。執行計劃由算子組成。因為 Hologres 的一個表的資料會根據 Distribution Key 分布在多個 Shard 上,每個 Shard 内又可以包含很多 Segment,執行計劃也會反映這樣的結構,并分布到資料所在的節點去執行。每個Table Shard 會被加載到一個計算節點,資料會被緩存到這個節點的記憶體和本地存儲。因為是存儲計算分離的架構,如果一個節點出錯,其服務的 Shard 可以被重新加載到任意一個計算節點,隻是相當于清空了緩存。

例如一個比較簡單的查詢。

select key, count(value) as total from table1 group by key order by total desc limit 100。      

如果是單機資料庫,可以用這樣的執行計劃。如果資料和計算分布在多個節點上,就需要更複雜的執行計劃。

Hologres揭秘:深度解析高效率分布式查詢引擎

在分布式表上,為了更高效地執行,盡量減少資料傳輸,可以把執行計劃分為不同片段(Fragment)分布到相應節點執行,并且把一些操作下推來減少 Fragment 輸出的資料,可能就變成這樣的執行計劃:

Hologres揭秘:深度解析高效率分布式查詢引擎

根據資料的特性,優化器可能會生成不同的計劃。例如在某一個局部聚合并沒有顯著減少資料量的時候,可以省略這個算子。又例如在 Key 就是 Distribution key 的時候,可以優化為:

Hologres揭秘:深度解析高效率分布式查詢引擎

從這些例子可以看出,Hologres 的執行計劃根據資料的特性切分為不同的片段之後分布式并發執行。片段之間通過 Exchange 算子進行資料交換。更複雜的比如多表關聯(Join)查詢,會有更多的片段和更複雜的資料交換模式。

比如以下SQL

select user_name, sum(value) as total from t1 join t2 on t1.user_id = t2.user_id where … group by user_name order by total limit 100      

在Hologres中可以是這樣的執行計劃

Hologres揭秘:深度解析高效率分布式查詢引擎

如果 Join key 和 Distribution Key 一緻,可以優化為如下執行計劃,減少遠端資料傳輸。根據需要的查詢合理地設定 Distribution Key,可能顯著提高查詢性能。

Hologres揭秘:深度解析高效率分布式查詢引擎

根據過濾條件和統計資訊等等,優化器還可能生成不同的優化執行計劃,比如包含動态過濾,局部聚合等等。

這樣的分布式執行計劃足夠通用,可以表達所有的 SQL 查詢和一些其它查詢。執行計劃和大部分 Massively Parallel Processing (MPP) 系統也比較類似,友善借鑒和內建業界的一些适用的優化。稍微獨特一些的地方是很多查詢計劃片段的執行個體是和 Hologres 的存儲結構對齊的,能夠進行高效的分區裁剪和檔案裁剪。

同時,Hologres 實作了 PostgreSQL 的 Explain 和 Explain Analyze 系列語句,可以展示文本格式的執行計劃和相應的執行資訊,友善使用者自助了解執行計劃,并針對性做出SQL優化調整。

全異步執行

高并發系統,特别是有大量 I/O 的系統,頻繁地等待或者任務切換是常見的系統瓶頸。異步處理是一種已經被證明行之有效的避免這些瓶頸,并把高并發系統性能推到極緻的方法。

Hologres 的整個後端,包括執行引擎、存儲引擎和其它元件,統一使用 HOS(Hologres Operation System) 元件提供的異步無鎖程式設計架構,能夠最大化異步執行的效果。每個 Fragment 的執行個體使用 HOS 的一個 EC (邏輯排程機關),使得一個 Fragment 裡的所有算子和存儲引擎可以異步執行并且無鎖安全通路絕大多數資源。

算子和 Fragment 都是類似這樣的接口:

future<> Open(const SeekParameters& parameters, ...)
future<RecordBatchPtr, bool> GetNext(...)
future<> Close(...)      

除了一般異步處理的好處外,異步算子接口較好地規避了存儲計算分離架構下相對較高的讀資料延遲對查詢性能的影響,并且對分布式查詢的執行模型本身也有獨特的好處。

DAG 執行引擎一般可以分為拉資料的模性(比如火山模型)和推的模型(比如很多大資料的分階段執行模型),各有其優缺點。而 Hologres采用的異步的拉模型能夠取得兩種模型的好處并且避免其缺點(已經申請了專利)。舉一個常見的 Hash Join 來說明:

Hologres揭秘:深度解析高效率分布式查詢引擎

火山模型可以簡單做到先拉完 b 的資料建構 hash table,然後流式處理 a 的資料不用全放在記憶體裡。但是當 a 或者 b 需要讀資料的時候,簡單的實作需要等待不能把 CPU 打滿,需要通過提高 Fragment 的并發數或者引入複雜的 pre-fetch 機制來充分利用資源,而這些又會引入别的性能問題。

推資料的模型,比較容易做到并發讀資料請求并在完成的時候觸發下遊處理,但是上述 Join算子的實作會比較複雜。比如 a 處理完一批資料推到 Join 算子而 b 的 hash table 還沒有建構完成,這批資料就需要暫存到記憶體裡或者盤上,或者引入反壓機制。在 Fragment 的邊界也會有類似問題,造成一些在拉資料模型下不需要的資料緩存。

Hologres 的算子和 Fragment 的異步拉資料模型,可以像火山模型一樣簡單做到按需從上遊擷取資料,而同時又可以像推資料模型一樣簡單做到讀資料并發,隻要向上遊發出多個異步 GetNext,上遊處理完成時會自然觸發後續處理。異步 GetNext 的數目和時機,可以看做是天然的流控機制,可以有效做到提高 CPU 使用率并且避免不必要的資料暫存。

Hologres 已經用這個異步模型實作了一個完整的查詢引擎,可以支援所有 PostgreSQL 的查詢。

列處理和向量化

 按列處理和向量化執行都是分析查詢引擎常用的優化機制,可以大幅度提高資料處理的效率。Hologres 也不例外,在能使用向量處理的時候盡量使用。

Hologres 在記憶體裡也采用列式存儲。在記憶體裡按列存儲資料能夠使用更多的向量處理。列式組織資料還有一個好處,就是對延遲計算比較友好。比如

select … where a = 1 and b = 2 …

,對一批資料(一般對應存儲的一個 row group),Hologres的 scan 算子輸出的 a 和 b 可以是延遲讀取的 a 和 b 的資訊,在處理 a = 1 的時候會讀取這一批的 a。如果 a=1 對這一批的所有行都不滿足,這一批的 b 這一列就根本不會被讀取。

但是對某些按行處理的算子,比如 Join,按列存儲的資料可能會造成更多的 CPU cache miss ,帶來較大的性能問題。很多查詢引擎會在不同的點引入按列存儲和按行存儲的轉換,但是頻繁的轉換本身會帶來不小的開銷,而且列轉行會造成上述延遲讀取列被不必要地讀取,還有一些其它的性能問題。

自适應增量處理

很多實時資料應用經常會對一個查詢用不同的時間段反複執行。比如一個監控名額頁面打開後,會定期執行

select avg(v1) from metrics where d1 = x and d2 = y and ts >= '2020-11-11 00:00:00' and ts < '2020-11-11 03:01:05' and … group by d3 …

這樣的查詢,下一次會改成

ts < '2020-11-11 00:03:10'

,再下一次

ts < '2020-11-11 00:03:15'

流計算或者增量計算可以對這種查詢進行非常高效的處理。但是對這種使用者可以随意生成的互動式查詢,通常不可能對所有組合都配置流計算或者增量計算任務。如果每次都簡單執行查詢,又可能有大量的重複計算造成資源浪費和性能不理想。

Hologres充分利用存儲引擎和計算引擎的深度內建和列式存儲大部分資料在隻讀檔案中的特性,在能提供包含最新寫入資料的查詢結果的同時盡量避免重複計算,對這種類型的查詢能夠顯著提升性能和減少資源使用。

針對特定查詢模式的深度優化

Hologres 對一些特定查詢模式有獨特的優化。這裡以Filter Aggregate 優化為例子。

很多資料應用都有開放列的需求,相當于可以動态添加邏輯列而不用改 Table Schema。比如有一列是多值列 tags(Postgres 可以用 Array 類型)裡面存了'{c1:v1, c2:u1}' 這樣的多個邏輯列的值。查詢的時候,如果使用普通列,一類常見的查詢是

-- Q1: 
select c1, sum(x) from t1 where c1 in (v1, v2, v3) and name = 'abc' group by c1      

使用開放列後,這樣的查詢會轉變為

-- Q2: 
select unnest(tags), sum(x) from t1 where name = 'abc' and tags && ARRAY['c1:v1', 'c1:v2', c1:v3'] 
group by unnest(tags) 
having unnest(tags) in ('c1:v1', 'c1:v2', c1:v3')      

這種查詢,Hologres 可以利用位圖索引快速計算過濾條件得到相關的行,但是之後從多值列裡面取出相關資料操作不能使用向量處理,性能不能達到最優。經過調研,可以把查詢的執行轉換為

Q3: 
select 'c1:v1', sum(x) from t1 where tags && ARRAY['c1:v1'] 
UNION ALL 
select 'c1:v2', sum(x) from t1 where tags && ARRAY['c1:v2'] 
UNION ALL
…      

這樣每個 UNION ALL 分支可以隻讀取 name 和 tags 的位圖索引計算過濾條件,然後用 x 列的資料和過濾條件進行向量計算 SUM_IF 即可得出想要的結果。這樣的問題是,每個分支都要過一遍 t1,讀取 x 列以及 name 列的位圖索引,帶來重複計算。最後引入了一個 filter aggregate 的特殊算子來把這類常用查詢優化到極緻性能,可以隻過一遍 t1 并且去掉重複操作,隻用向量計算即可得到結果,不需要讀取 tags 列的資料。在一個幾十 TB的表上實測性能提升 3 倍以上。

類似的優化,Hologres 的執行引擎都會盡量抽象為比較通用的算子,可以适用于更多場景。Filter Aggregate 算子也是 Hologres 申請的專利之一。

總結

Hologres 執行引擎在一個架構裡集中了相關分布式查詢系統的幾乎所有最高效的優化方式(包括各種類型的索引)并作出了特有的改進。通過和存儲引擎深度整合,能充分發揮異步模型的優勢,并高效利用各種類型的索引來加速查詢。所有這些加起來,帶來了超越已有系統的性能,并在阿裡巴巴雙 11 的資料規模下通過了實戰的考驗,(2020年雙11頂住了5.96億/秒的實時資料洪峰,基于萬億級資料對外提供多元分析和服務,99.99%的查詢可以在80ms以内傳回結果),對外高并發高性能地提供分布式 HSAP 查詢服務。

後續我們将會陸續推出有關Hologres的技術底層原理揭秘系列,具體規劃如下,敬請持續關注!

  • Hologres揭秘:深度解析高效率分布式查詢引擎(本文)
  • Hologres揭秘:透明加速MaxCompute查詢核心原理
  • Hologres揭秘:如何實作MaxCompute與Hologres資料同步速度快百倍
  • Hologres揭秘:如何支援高吞吐Upsert
  • Hologres揭秘:如何支援線上服務場景的超高QPS
  • Hologres揭秘:如何支援高并發查詢
  • Hologres揭秘:如何支援高可用架構
  • Hologres揭秘:如何支援資源隔離,支援多種負載
  • Hologres揭秘:向量檢索引擎Proxima原理與使用實踐
  • Hologres揭秘:讀懂執行計劃,查詢性能翻十倍
  • Hologres揭秘:分布式系統如何設計Shard與Table Group
  • Hologres揭秘:如何支援更多Postgres生态擴充包
  • Hologres揭秘:高吞吐寫入Hologres的N種姿勢
  • ......

感謝您的閱讀,也歡迎試用體驗Hologres,可以參考

使用手冊

,同時也歡迎掃碼加入釘群進行技術交流:

Hologres揭秘:深度解析高效率分布式查詢引擎