Apache Kylin基本原理與常見優化

一、基本原理

Apache Kylin是個開源分佈式OLAP引擎,其基本原理是數據立方和預計算

Kylin一般是作爲數據倉庫的應用層引擎,對業務提供SQL查詢分析服務,針對數據維度多、數據基數大的場景,Kylin預計算可以保證在毫秒級時間返回分析結果,查詢階段性能十分出衆。

Kylin的相關人員主要分爲三種角色:1. 數據用戶 2. 數據倉庫建模人員 3. 數據平臺管理員。數據用戶通過BI可視化分析工具或者編寫SQL查詢Kylin的數據; 數據倉庫建模人員維護Kylin元數據,設計如何構建Cube,選擇維度、度量;數據平臺管理員提供存儲和計算平臺支持,目前最常見的存儲引擎是HBase,計算引擎是Spark。

在這裏插入圖片描述

1. 基礎模型:Star Schema

我們以電商場景爲例具體看一下如何設計和構建Kylin Cube。下圖是一個典型數據倉庫中的星型模型,模型的中心是事實表 fact_sales,模型的周圍是五張維度表:dim_date,dim_product, dim_store,dim_promotion,dim_customer,它們通過代理鍵和事實表相關聯;

在這裏插入圖片描述

在這個星型模型下,有一個需求,“分析電商網站用戶在一週的每一天中,更傾向於購買Fresh Fruit還是Candy”;

終端用戶用SQL實現如下查詢:

SELECT
dim_date.weekday, dim_product.category,
SUM(fact_sales.quantity) AS quantity_sold
FROM fact_sales
JOIN dim_date ON fact_sales.date_key = dim_date.date_key
JOIN dim_product ON fact_sales.product_sk = dim_product.product_sk
WHERE
dim_date.year = 2013 AND
dim_product.category IN ('Fresh fruit', 'Candy')
GROUP BY
dim_date.weekday, dim_product.category;

備註:Kylin也支持其他數據倉庫模型,但大多數數據倉庫場景都是星型模型;本文爲了簡單起見,不展開討論數據倉庫建模。

2. 基本原理:預計算

從最簡單的情況開始,我們構建一個OLAP Cube,它只包含date_key和product_sk兩個維度,如下圖所示
在這裏插入圖片描述

假設存儲引擎爲HBase,物理上看,一個Cube的存儲內容如下所示,其中RowKey是維度取值的拼接,Value是SUM(net_price);

Base Cuboid的RowKey SUM(net_price)
140101_32 149.60
140101_33 31.01
140101_34 84.58
140101_35 28.18
140102_32 132.18
140102_33 19.78
140102_34 82.91
140102_35 10.96

3. 列存儲

在這裏插入圖片描述

簡單說,HBase的表是rowkey有序的多層map,如果不熟悉HBase存儲結構,推薦閱讀這篇:深入理解HBase系統架構

4. 壓縮編碼

Kylin默認對所有維度列做壓縮編碼,編碼格式一般是bitmap全局字典。如果某一個維度有特定的含義,可以手動指定編碼類型,如date、int等。

在這裏插入圖片描述

二、Cube建模優化

Cube建模優化的核心是cuboid剪枝,避免不必要的維度組合,減少kylin預計算cuboid的數量;在不優化的情況下,一個n個維度的Cube存在2^(n)種維度組合,cuboid個數隨着維度個數指數增長,很容易出現維度爆炸。

1. 維度

(1). 聚集組

含義:如果定義了聚集組G, 組內包含{A, B, C, …} 若干維度,那麼在創建Cube時,只有組內維度組合的cuboid會被創建,組外維度不會預先創建cuboid

舉例:例如cube定義了30個維度,定義了3個聚集組,每組包括10個維度。定義聚集組後,需要預計算的cuboid個數爲 2^{10} + 2^{10} + 2{10}。如果沒有定義聚集組,需要預計算2{30}個cuboid。

(2). 強制維度

含義:必選維度、所有查詢都必須包含該字段,例如日期;

效果:假設聚集組中有n個維度,其中包含m個強制維度,那麼cuboid個數從 2^(n)優化爲 2^(n-m)個

舉例:把日期字段dt作爲強制維度,所有不包含dt的維度組合都不會被預計算,從而cuboid個數可以減半。

在這裏插入圖片描述

(3). 層次維度

含義:維度有上下層級關係,查詢時一般按上下級維度進行“上卷”、“下鑽”,例如組織架構,標籤結構;

效果:假設有一組n個維度,定義層次維度前,cuboid個數是2^{n}, 把這n個維度定義成層次維度後,cuboid個數是n,即層次維度把指數增長變成線性增長;

舉例:假如存在“省”,“市”,“縣”,“街道”四個維度,定義了一個層次維度,包括“省”,“市”,“縣”,“街道”,那麼cuboid個數從2^{4} 減少爲4個,分別是{“省”,“市”,“縣”,“街道”}, {“省”,“市”,“縣”}, {“省”,“市”}, {“省”} 。

在這裏插入圖片描述

(4). 聯合維度

含義:如果定義了聯合維度A, B, …, X, 那麼Kylin預計算cuboid時會把{A,B,…,X}當做一個維度,他們或者同時出現,或者同時不出現;

效果:相當於把聯合維度中的n個維度縮減成了一個維度,預計算量比定義聯合維度前減少了2^{n-1}

舉例:有兩種常用場景: 第一種是xxx_id, xxx_name, 使用時按id查詢,按name顯示,這種情況適合把id和name作爲一個聯合維度;第二種是cube維度中, 存在一些低頻查詢維度X, Y, Z,這時候把X, Y, Z作爲一個聯合維度,可以減少預計算量。

(5). 衍生維度

含義:如果維度配置了lookup table, 某些維度可以通過主鍵字段衍生到固定的取值,可以給這些維度配置衍生維度

效果:假如衍生維度個數爲n,則預計算cuboid的個數從2{n}減少到2{1}

舉例:例如可以給電商銷售事實表中的商品維度定義一個lookup table, 商品SK作爲外鍵,這時商品的屬性維度可以縮減到一個

在這裏插入圖片描述

2. 度量

(1). Dimension or Measure

首先,避免在cube中引入高基數維度,如果需求是對改字段進行去重,應該將該字段配置成Measure而不是dimension;

其次,實在需要按該字段過濾篩選,應該將字段配置成fixed_length編碼;

(2). Extended Column

一個id,一個name,查詢時按id匹配,顯示時按name做展示,這種情況可以把name作爲id的extended column。和聯合維度類似,可以簡單認爲id, name從2個維度變成了1個維度。

3. Cube Planner

Cube Planner是Kylin 2.3版本推出的功能,它可以自動地對Cube結構進行優化。

Cube Planner優化分爲兩個階段。第一階段發生在初次構建Cube時:Cube Planner會利用在“Extract FactTable Distinct Columns”步驟中得到的採樣數據,預估每個Cuboid的大小,進而計算出每個Cuboid的效益比(該Cuboid的查詢成本/對應維度組合物化後對整個Cube的所有查詢能減少的查詢成本)。Cube Planner只會對那些效益比更高的維度組合進行預計算,而捨棄那些效益比更低的維度組合。第二階段作用於已經運行一段時間的Cube。在這一階段,Cube Planner會從System Cube中獲取該Cube的查詢統計數據,並根據被查詢命中的概率給Cuboid賦予一定權重。當用戶觸發對Cube的優化操作時,那些幾乎不被查詢命中的Cuboid會被刪除,而那些被頻繁查詢卻尚未被預計算出的Cuboid則會被計算並更新到Cube中。

關於Cube Planner更多細節,請參考Kylin官方網站相關文檔

三、HBase存儲優化

Kylin是可插拔式架構,它依賴的存儲引擎,計算引擎都是面向接口的,不依賴於特定引擎。開源版本的存儲引擎是HBase,本文主要以HBase存儲爲例介紹常見的存儲層面的優化思路。

1. rowkey順序

前提:hbase存儲, rowkey是多個字段拼接,hbase的基本原理是在相同region中rowkey有序;在kylin查詢中,預計算結果按rowkey進行查找;

例子:rowkey取值d1_d2_d3,相同基數的情況下,按d1維度過濾的效率要高於d2, 按d2維度過濾的效率要高於d3;基數多的字段,能過濾掉更多數據量的字段,更值得放在rowkey的前面;

2. 維度編碼

含義:用編碼代替原始值,減少cube存儲大小;根據字段基數選擇相適應的編碼類型;一般默認選擇字典編碼(dict),個別字段基數非常大(百萬量級),可以替換成固定長度編碼(fixed_length);

3. Shard by column

如果一列基數足夠高,例如幾百萬,那可以把相同取值的數據在相同的hbase region中存儲,提高存儲本地性,從而提高計算效率;

四、OLAP引擎對比

講道理,Apache Kylin是很有中國特色的一個開源項目,主要是指項目發起人、主要開發者都是中國人,因此也存在一些特有的問題,比如項目的英文文檔讀起來就不那麼通順,這一方面是英文寫作行文語法的問題,另一方面是理論深度的問題。理論上的問題是項目的根基,決定了項目的生命力,項目能走多遠很大程度上取決於它的理論基礎是否牢靠。

1. Kylin和Doris

Doris是百度發起的項目,是另外一個國人主導的開源OLAP項目。Doris的前身是百度PALO,總體是參考Google Mesa項目進行搭建的。

Doris是一個MPP的OLAP項目,和Kylin的主要區別是它是查詢時計算的引擎,同時利用索引提升性能。Doris不需要預計算,不需要數倉建模人員預定義模型,因此上手難度要比Kylin小很多。

和Doris相比,Kylin目前的代碼擴展性更好,因爲Kylin的整體設計是面向接口的,存儲引擎,計算引擎,這些都是可更換的。例如,早期Kylin的代碼是基於Hadoop的MapReduce計算引擎,基於HBase的存儲引擎,但是隨着大數據技術的發展,MapReduce實際在很多任務的性能上不如Spark,這時候Kylin把計算引擎更換成Spark,可以讓Kylin持續保持競爭力。再比如,Kylin的存儲引擎HBase,實際上在HBase Region數大到一定規模的時候,會遇到難以橫向擴展的問題,這時候如果能做到存儲引擎替換,對Parquet、ORC這些格式做一定的改造,或者基於Druid,基於Delta Lake等項目進行融合,這時候Kylin依然可以保持競爭力。Doris在這方面是分成前臺和後臺兩大模塊,從系統架構上來看,模塊劃分不夠清晰,沒有做到高內聚、低耦合。

2. Kylin和ES

我的工作經歷是,數據量沒那麼大的時候,統統往ES裏面錄入,使用時寫ES查詢和聚合語句,流程很順暢。ES的文檔算得上業界良心了,入門文檔上手非常快,上手後深入一些的文檔也都能準確地說明要點,內容安排恰到好處,用戶體驗很不錯。

但是ES也會遇到它的問題,就是用戶需要從存儲層重新設計結構。原理上講,ES的本質是倒排索引,你查詢的是索引而不是原始數據;這個存儲模型導致了使用ES時你需要仔細設計索引的存儲結構,尤其是關聯文檔,是用嵌套文檔還是父子文檔,一不小心,父子文檔查詢和聚合的性能可能就不滿足你的需求了。另外,ES容易遇到全表掃描,集羣假死的問題,需要想辦法在應用層面規避用戶的全表掃描。

3. Kylin和Druid

我之前的工作經歷中,實際應用到Druid的項目並不多,沒有太多實踐經驗可以分享,簡單從概念上對比一下吧。Druid的核心思想是數據錄入時做輕度彙總,查詢時做計算。在預計算和MPP之間,Druid更接近MPP一些。和Kylin相比,它的數據延遲明顯更低,因此Kylin主要是做離線計算,Druid在實時的場景中應用更多。另一方面,Druid在數據錄入時做輕度彙總,這個彙總的粒度取決於用戶指定的查詢粒度,這個粒度可以選擇毫秒/秒/分鐘,等各種級別,看你的具體使用場景需求。

最後關注一下大家的共性,絕大多數OLAP項目都用到了列存儲字典編碼位圖索引等技術。這是因爲大數據項目中提前剪枝掉一部分數據就等於節約了大量的數據讀取、中轉、加工成本,能給系統帶來巨大的性能提升。列存儲是在剪枝掉用不到的列,字典編碼更緊湊地存儲了維度列。位圖索引是在通過布爾運算快速同時匹配多個過濾條件,避免了對一份數據集多次過濾後再對過濾結果進一步執行布爾運算。

五、參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章