kylin簡單優化cube
優化Cube
層次結構
理論上,對於N維,你最終會得到2 ^ N維組合。但是對於某些維度組,不需要建立這麼多組合。例如,如果您有三個維度:洲,國家,城市(在層次結構中,“更大”維度首先出現)。在深入分析時,您只需要以下三種組合組合:
按大陸分組
按大陸,國家分組
按大陸,國家,城市分組
在這種情況下,組合計數從2 ^ 3 = 8減少到3,這是一個很好的優化。 YEAR,QUATER,MONTH,DATE案例也是如此。
派生列
派生列用於一個或多個維度(它們必須是查詢表上的維度,這些列稱為“派生”)可以從另一個維度推匯出來(通常它是相應的FK,這稱為“主機列”)
例如,假設我們有一個查詢表,我們將其連線到事實表,並將其與“其中DimA = DimX”。請注意,在Kylin中,如果您選擇FK為維度,相應的PK將自動排隊,無需任何額外費用。祕訣是,由於FK和PK總是相同的,Kylin可以先在FK上應用過濾器/ groupby,然後將它們透明地替換為PK。這表明如果我們想在我們的立方體中使用DimA(FK),DimX(PK),DimB,DimC,我們可以安全地選擇DimA,DimB,DimC。
事實表(連線)查詢表
column1,column2 ,,,,,, DimA(FK)DimX(PK),, DimB,DimC
假設DimA(代表FK / PK的維度)具有到DimB的特殊對映:
dimA dimB dimC
1 a ?
2 b ?
3 c ?
4 a ?
在這種情況下,給定DimA中的值,確定DimB的值,因此我們說dimB可以從DimA匯出。當我們構建一個包含DimA和DimB的多維資料集時,我們簡單地包含DimA,並將DimB標記為派生。派生列(DimB)不參與長方體生成:
原創組合:
ABC,AB,AC,BC,A,B,C
從A到B時的組合:
AC,A,C
在執行時,如果查詢類似於“select count(*) from fact_table inner join looup1 group by looup1 .dimB”,則期望包含DimB的長方體來回答查詢。但是,由於派生優化,DimB將出現在NONE的長方體中。在這種情況下,我們首先修改執行計劃以使其由DimA(其主機列)進行分組,我們將得到如下的中間答案:
DIMA COUNT(*)
1 1
2 1
3 1
4 1
之後,Kylin將用DimB值替換DimA值(因為它們的值都在查詢表中,Kylin可以將整個查詢表載入到記憶體中併為它們構建對映),並且中間結果變為:
DimB count(*)
a 1
b 1
c 1
a 1
在此之後,執行時SQL引擎將進一步將中間結果聚合為:
DimB count(*)
a 2
b 1
c 1
這一步發生在查詢執行時,這意味著“以額外的執行時聚合為代價”
效能優化
分割槽列優化
如果cube的分割槽列與Hive表的分割槽列相同,那麼根據它過濾資料能讓Hive聰明地跳過不匹配的分割槽。因此強烈建議用Hive的分割槽列(如果它是日期列)作為cube的分割槽列。這對於那些資料量很大的表來說幾乎是必須的,否則Hive不得不每次在這步掃描全部檔案,消耗非常長的時間。
檔案合併
如果啟用了Hive的檔案合併,你可以在conf/kylin_hive_conf.xml裡關閉它,因為Kylin有自己合併檔案的方法(下一節):
<property>
<name>hive.merge.mapfiles</name>
<value>false</value>
<description>Disable Hive's auto merge</description>
</property>
重新分發中間表
Hive在HDFS上的目錄裡生成了資料檔案:有些是大檔案,有些是小檔案甚至空檔案。這種不平衡的檔案分佈會導致之後的MR任務出現數據傾斜的問題:有些mapper完成得很快,但其他的就很慢。針對這個問題,Kylin增加了這一個步驟來“重新分發”資料,這是示例輸出:
total input rows = 159869711
expected input rows per mapper = 1000000
num reducers for RedistributeFlatHiveTableStep = 160
重新分發表的命令:
hive -e "USE default;
SET dfs.replication=2;
SET hive.exec.compress.output=true;
SET hive.auto.convert.join.noconditionaltask=true;
SET hive.auto.convert.join.noconditionaltask.size=100000000;
SET mapreduce.job.split.metainfo.maxsize=-1;
set mapreduce.job.reduces=160;
set hive.merge.mapredfiles=false;
INSERT OVERWRITE TABLE kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34 SELECT * FROM kylin_intermediate_airline_cube_v3610f668a3cdb437e8373c034430f6c34 DISTRIBUTE BY RAND();"
首先,Kylin計算出中間表的行數,然後基於行數的大小算出重新分發資料需要的檔案數。預設情況下,Kylin為每一百萬行分配一個檔案。在這個例子中,有1.6億行和160個reducer,每個reducer會寫一個檔案。在接下來對這張表進行的MR步驟裡,Hadoop會啟動和檔案相同數量的mapper來處理資料(通常一百萬行資料比一個HDFS資料塊要小)。如果你的日常資料量沒有這麼大或者Hadoop叢集有足夠的資源,你或許想要更多的併發數,這時可以將conf/kylin.properties
裡以下配置設為小一點的數值,比如:
kylin.job.mapreduce.mapper.input.rows=500000
其次,Kylin會執行 “INSERT OVERWRITE TABLE … DISTRIBUTE BY “ 形式的HiveQL來分發資料到指定數量的reducer上。在很多情況下,Kylin請求Hive隨機分發資料到reducer,然後得到大小相近的檔案,分發的語句是”DISTRIBUTE BY RAND()”。
如果你的cube指定了一個高基數的列,比如”USER_ID”,作為”分片”維度(在cube的“高階設定”頁面),Kylin會讓Hive根據該列的值重新分發資料,那麼在該列有著相同值的行將被分發到同一個檔案。這比隨機要分發要好得多,因為不僅重新分佈了資料,並且在沒有額外代價的情況下對資料進行了預先分類,如此一來接下來的cube build處理會從中受益。在典型的場景下,這樣優化可以減少40%的build時長。在這個案例中分發的語句是”DISTRIBUTE BY USER_ID”:
請注意: 1)“分片”列應該是高基數的維度列,並且它會出現在很多的cuboid中(不只是出現在少數的cuboid)。 使用它來合理進行分發可以在每個時間範圍內的資料均勻分佈,否則會造成資料傾斜,從而降低build效率。典型的正面例子是:“USER_ID”、“SELLER_ID”、“PRODUCT”、“CELL_NUMBER”等等,這些列的基數應該大於一千(遠大於reducer的數量)。 2)”分片”對cube的儲存同樣有好處,不過這超出了本文的範圍。
將cuboid資料轉換為HFile
這一步啟動一個MR任務來講cuboid檔案(序列檔案格式)轉換為HBase的HFile格式。Kylin通過cube統計資料計算HBase的region數目,預設情況下每5GB資料對應一個region。Region越多,MR使用的reducer也會越多。如果你觀察到reducer數目較小且效能較差,你可以將“conf/kylin.properties”裡的以下引數設小一點,比如:
kylin.hbase.region.cut=2
kylin.hbase.hfile.size.gb=1
rowkey構建
對rowkey的構建也有一定的要求,一般而言,需要把基數大的欄位放在前面,這樣可以在scan的過程中儘可能的跳過更多的rowkey。
另一方面將基數小的列放在rowkey的後面,可以減少構建的重複計算,有些cuboid可以通過一個以上的父cuboid聚合而成,在這種情況下,Kylin將會選擇最小的父cuboid。例如,AB能夠通過ABC(id:1110)和ABD(id:1101)聚合生成,因此ABD會被作為父cuboid使用,因為它的id比ABC要小。基於以上處理,如果D的基數很小,那麼此次聚合操作就會花費很小的代價。因此,當設計cube的rowkey順序的時候,請記住,將低基數的維度列放在尾部。這不僅對cube的構建過程有好處,而且對cube查詢也有好處,因為後聚合(應該是指在HBase查詢對應cuboid的過程)也遵循這個規則。
資料轉換為HFile
kylin將生成的cube通過生成HFile的方式匯入到hbase,這個優化點可以配置hbase的相關引數。
- region數量預設是1,如果資料量大的話可以提高region數量
- region大小預設是5GB,也就是hbae官方建議的大小;如果cube大小比這個值小太多,可以減小單region的大小
- hfile檔案大小,預設是1GB,由於是通過mapreduce寫入的,小檔案意味著寫入快,但是讀取慢,大檔案意味著寫入慢,讀取快
經驗
- 儘量將需要展現的欄位作為維度,沒必要所有的一股腦加進去。
- 每次查詢或者要經常group by的欄位作為Mandatory維度。且該維度放在 rowkey的最前面。
- 將數量相近也就是說某兩個欄位通過select count("欄位名")獲取的結果近似1:1,設定為joint維度。
- rowkey的順序按查詢頻率從高到低,從前往後排。
- 將經常出現在同一SQL中的不同維度放置在一個維度組中,將從不出現在一個SQL查詢中的不同維度設定在不同的維度組中。
- Dictionary預設為dict型別,如果某個欄位中的值非常大(小幽遇到過的一個欄位中的值儲存成文字足足有23Kb!!!),大到以至或者可能使得Cube在build過程中出現OOM的錯誤,則需要將該欄位的值設定為fixed_length型別,取可以展現這個維度的前length個位元組,比如對於之前那個23kb的欄位值,經和業務人員協商,發現取前4000個位元組就可以表示這個欄位了。所以fixed_length的值設定為4000.值得一提的是,Dictionary預設為false,是不給該欄位在記憶體中建立詞典樹的,而更改為true則表示給該欄位建立詞典樹。有詞典樹,則會優化帶有該欄位的SQL查詢,提升查詢速度,但相應地也會消耗一些記憶體。
總結
基於kylin的ui,可以看到kylin在構建cube時各個流程的耗時,可以依據這些耗時做相應的優化,常見的,可以從耗時最長的步驟開始優化,比如:
- 遇到建立hive中間表時間很長,考慮對hive表進行分割槽處理,對錶中的檔案格式更改,使用orc,parquet等高效能的檔案格式
- 遇到cube構建時間過長,檢視cube設計是否合理,維度的組合關係是否可以再減少,構建引擎是否可以優化
分享一個其他得cube優化設計的推薦:https://www.cnblogs.com/wenBlog/p/10255467.html