Kylin 上手
根據Kylin 官方給出的測試數據,我們實際操作一下 Kylin。
1. 導入 Hive 數據
首先創建一個project,在界面左上角有個“Add Project” 按鈕,這里我們創建的Project名為tuto。
進入Model 界面,從 Hive 中導入兩張表:kylin_sales和 kylin_cal_dt
導入完成后可以在左邊看到表的定義:
同時 Kylin 會在后台觸發一個 MapReduce 任務,計算此表中每個列的基數。一般在隔幾分鍾后再刷新頁面,就可以看到基數信息,如:
需要注意的是,這里 Kylin 對基數采用的是HyperLogLog的近似算法,與精確值略有誤差。例如對TRANS_ID 的基數估計為 10491,而若是我們在Hive 中獲取 TRANS_ID 的distinct count:
select count(distinct trans_id) from kylin_sales;
> 10000
可以看到計算的結果與kylin計算出的基數稍有誤差,不過一般這種近似值作為參考值是足夠的。
2. 創建數據模型
2.1. 選擇事實表與維度表
數據模型(Data Model)是Cube 的基礎,主要根據分析需求進行設計。有了數據模型后,定義Cube的時候就可以直接從此模型定義的表和列中進行選擇了,省去了重復指定連接(JOIN)條件的步驟。基於一個數據模型可以創建多個Cube,方便減少用戶的重復性工作。
在 Kylin web 界面的 Model 頁面,創建一個新Model(也就是數據模型)。必須選擇一個事實表,以及(可選)添加一個維度表。
這里snapshot是指是否將維度表以快照(snapshot)的形式存儲到內存中以供查詢。當維度表小於300MB時,推薦啟用維度表以快照形式存儲,以簡化Cube計算和提高系統整體效率。當維度表超過 300MB 時,則建議關閉維度表快照,以提升Cube構建的穩定性與查詢的性能。
對於join key,也支持多組key。
2.2. 選擇維度和度量
選擇維度和度量的列,這里只是選擇一個范圍,並不代表這些列將來一定要用作 Cube 的維度或度量。可以把所有可能會用到的列都選進來,后續創建 Cube 的時候,將只能從這些列中進行選擇。
選擇維度列時,維度可以來自事實表或維度表,例如:
選擇度量時,度量只能來自事實表或不加載進內存的維度表,如:
最后一步,是為模型補充分割時間列信息和過濾條件。如果此模型中的事實表記錄是按時間增長的,那么可以指定一個日期/時間列作為模型的分割時間列,從而可以讓Cube按此列做增量構建。
過濾(Filter)條件是指,如果想把一些記錄忽略掉,那么這里可以設置一個過濾條件。Apache Kylin 在向 Hive 請求源數據的時候,會帶上此過濾條件,如下所示,會只保留price 大於 0 的記錄:
點擊 Save 后,即可保存此 Model。
3. 創建 Cube
3.1. Cube Info
從左上角創建按鈕新創建一個 Cube,並選擇之前創建的Model:
3.2. Dimensions
然后添加Cube的維度:
3.3. Measures
接着是添加度量,Kylin支持的度量有SUM、MIN、MAX、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE等。默認會創建一個Count(1) 度量。
可以通過Bulk Add Measure 按鈕批量添加度量。目前對於批量添加度量,Kylin僅支持SUM、MIN、MAX等簡單函數。只需要選擇度量類型,然后再選擇需要計算的列即可:
如果需要添加復雜度量,點擊“+Measure”按鈕添加即可,如:
添加完后的結果為:
3.4. Refresh Setting
接下來是關於Cube數據刷新的設置,這里可以設置自動合並的閾值、自動合並觸發時保留的閾值、數據保留的最小時間、以及第一個Segment的起點時間(如果Cube有分割時間列):
3.5. Advanced Setting
在此頁面可以設置維度聚合組和Rowkey屬性。
默認Kylin會把所有維度放在同一個聚合組(Aggregation Group,也稱維度組)中,如果維度數較多(如 > 15),建議用戶根據查詢的習慣和模式,點擊New Aggregation Group+ 命令,將維度分布到多個聚合組中。通過使用多個聚合組,可以大大降低Cube中Cuboid數量。
舉個例子,一個Cube有(M+N)個維度,如果把這些維度都放置在一個組里,那么默認會有2(M+N) 個Cuboid;如果把這些維度分為兩個不相交的聚合組,第一個組有M個維度,第二個組有N個維度,那么Cuboid的總數量將減少至2M + 2N ,會極大減少Cuboid的數量。
在單個聚合組中,可以對維度設置一些高級屬性,如Mandatory Dimensions、Hierarchy Dimensions、Joint Dimensions 等。這幾種屬性都是為了優化Cube的計算而設計:
- Mandatory Dimensions(強制維度):指的是那些總是會出現在Where條件或Group By 語句里的維度。在指定某個維度為Mandatory Dimensions 后,Kylin可以不予計算那些不包含此維度的Cuboid,從而減少計算量
- Hierarchy Dimensions(層級維度):指一組有層級關系的維度,如“國家”、“省”、“市”。這里“國家“是最高級別的維度,”省“、”市“依次是次要級別。用戶為按高級別維度進行查詢,也會按低級別維度進行查詢。但當查詢低級別維度時,往往會帶上高級別維度的條件,而不會孤立地查詢低維度的數據。例如,用戶會使用”國家“作為條件進行查詢,也會使用”國家“+”省“+”市“的條件進行查詢。通過指定層級維度,Kylin可以略過不滿足此模式的Cuboid
- Joint Dimensions(聯合維度):將多個維度視作為一個維度,在進行組合計算的時候,它們要們一起出現,要們均不出現。通常適用於以下幾種情況:
- 總是一起查詢的維度
- 彼此之間有一定映射關系,如USER_ID 和 EMAIL
- 基數很低的維度,如性別、布爾類型的屬性
在此步驟中,web 界面里自動選擇了要添加的維度到此聚合組中。然后用戶可以根據模型特征和查詢模式,設置高級維度屬性。Hierarchy Dimension 和 Joint Dimension 可以設置多組,但是要主要的是,一個維度出現在某個屬性中后,將不能再設置為另一種屬性。但是一個維度是可以同時出現在多個聚合組中的。
添加完后的聚合組為:
Kylin中,會將Cube的構建結果以Key-Value的形式存儲到HBase中。而HBase是單行鍵索引,所以在存儲時,但是Kylin又需要按照多個維度來對度量進行檢索。所以在存儲到HBase中時,Kylin會將多個維度拼接組成Rowkey。在HBase中的存儲結構如下:
可以看到Dimensions 是rowkey,Metrics是value。由於同一維度中的數值長短不一,如國家名,短的如“中國”,長的如“巴布亞新幾內亞”,因此將多個不同列的值進行拼接的時候,要么添加分隔符,要么通過某種編碼使各個列所占的寬固定。Kylin為了能夠在HBase上高效地進行存儲和檢索,會使用第二種方式對維度值進行編碼。維度編碼的優勢有:
- 壓縮信息存儲空間;
- 提高掃描效率,減少解析開銷。
對於各個維度在rowkey中的順序,也會對查詢的性能產生比較明顯的影響。在這里用戶可以根據查詢的模式和習慣,通過拖拽的方式調整各個維度在Rowkey上的順序:
一般原則是:將過濾頻率高的列放置在過濾頻率低的列之前,將基數高的列放在基數低的列之前。這樣做的好處是:充分利用過濾條件來縮小在HBase中掃描的范圍,從而提高查詢的效率。
在構建Cube時,可以通過維度組合白名單(Mandatory Cuboids)確保想要構建的Cuboid能被成功構建:
Kylin支持對於復雜的COUNT DISTINCT度量進行字典構建,以保證查詢性能。目前提供兩種字典格式:Global Dictionary 和 Segment Dictionary。如下所示:
其中,Global Dictionary 可以將一個非Integer的值轉為Integer值,以便bitmap進行去重,如果要計算的COUNT DISTINCT的列本身已經是Integer類型,那就不需要定義Global Dictionary。並且Global Dictionary會被所有segment共享,因此支持跨segments做上卷去重操作。
而Segment Dictionary 雖然也是用於精確計算COUNT DISTINCT 的字典,但與Global Dictionary不同的是,它是基於一個segment的值構建的,因此不支持跨segments的匯總計算。如果你的cube 不是分區的,或者能保證你的所有SQL按照partition column 進行group by,那么最好使用Segment Dictionary 而不是 Global Dictionary,這樣可以避免單個字典過大的問題。
Kylin 目前支持兩種構建Cube 的引擎:Mapreduce 和 Spark。如果你的Cube只有簡單度量(如SUM、MIN、MAX),建議使用Spark。如果Cube中有復雜類型度量(如COUNT DISTINCT、TOP_N),建議使用MapReduce。
為了提升構建性能,可以在Advanced Snapshot Table中將維度表設置為全局維度表,同時提供不同的存儲類型:
在構建時,可以在Advanced Column Family 中對度量進行分組,如果有超過一個的COUNT DISTINCT或 Top_N 度量,可以將它們放在更多的列簇中,以優化HBase的I/O:
3.6. Configuration Overwrites
在這里可以為 Cube 配置參數。首先Kylin的全局參數在conf/kylin.properties 文件中;如果Cube需要覆蓋全局設置的話,需要在此指定。
例如,指定 kylin.hbase.region.cut 的值為 1,則此Cube在存儲的時候,Kylin將會按每個HTable Region 存儲空間為 1GB 來創建HTable Region。如果用戶希望任務從YARN 中獲取更多內存,可以設置kylin.engine.mr.config-override.mapreduce.map.memory.mb、kylin.engin.mr.config-override.mapreduce.map.java.opts 等mapreduce 相關參數。如果用戶希望Cube的構建任務使用不同的YARN資源隊列,可以設置kylin.engine.mr.config-override.mapreduce.job.queuename。這些配置均可以在Cube級別重寫。
配置完后保存,Cube 就創建完畢了。
4. 構建Cube
新創建的Cube只有定義,沒有計算的數據,狀態還是DISABLED,所以不會被查詢引擎挑中。為了讓Cube有數據,需要對它進行構建。構建有兩種:全量與增量。兩者構建步驟完全一樣,區別在於構建時讀取的數據源是全集還是子集。
Cube的構建過程如下,由任務引擎調度執行:
- 創建臨時Hive平表(從Hive中讀取數據)
- 計算各維度的不同值,並收集各Cuboid的統計數據
- 創建並保存字典
- 保存Cuboid統計信息
- 創建HTable
- 計算Cube(一輪或若干輪計算)
- 將Cube計算結果轉為HFile
- 加載HFile到HBase
- 更新Cube元數據
- 垃圾回收
上述步驟中,前5步是為了計算Cube而作的准備工作,如遍歷維度值來創建字典,對數據做統計和估算以創建HTable等。第6步是真正的Cube計算,取決於使用的Cube算法,它可能是一輪MapReduce任務,也可能是N(在沒有優化的情況下,N可以被視作維度數)輪迭代的MapReduce。
由於Cube運算的中間結果是以SequenceFile的格式存儲在HDFS上的,所以為了導入HBase,還需要進行第7步操作,將這些結果轉換成HFile。第8步通過HBase BulkLoad 將HFile導入到HBase 集群,這一步完成后,HTable就可以查詢到數據。第9步更新Cube的數據,將此構建的Segment狀態從“NEW”更新為“READY”,表示已經可供查詢。最后一步,清理構建過程中生成的臨時文件等垃圾,釋放集群資源。
Monitor頁面會顯示當前項目下進氣的構建任務
4.1. 全量構建與增量構建
對數據模型中沒有指定分割時間列信息的Cube,Kylin會采用全量構建,即每次都從Hive中讀取全部的數據來開始構建。通常適用於以下兩種情形:
- 事實表的數據不是按時間增長的
- 事實表的數據比較小或更新頻率很低,全量構建不會造成太大的存儲空間浪費
進行增量構建的時候,Kylin每次會從Hive中讀取一個時間范圍內的數據,然后進行計算,並以一個Segment的形式保存。下次構建的時候,自動以上此結束的時間為起點時間,再選擇新的終止時間進行構建。經過多次構建后,Cube中會有多個Segment依次按時間順序進行排列,如Seg-1,Seg-2,…,Seg-N。進行查詢的時候,Kylin會查詢一個或多個Segment然后做聚合計算,以便返回正確的結果給請求者。
使用增量構建的優勢是,每次只需要對新增數據進行計算,避免了對實例數據重復計算。對於數據量很大的Cube,使用增量計算是必須的。
下面是為構建一個Segment的Cube的數據框,需要用戶選擇時間范圍:
在從Hive中讀取源數據的時候,Kylin會帶上此條件。構建完畢后,Cube成為Ready狀態:
4.2. 歷史數據刷新
Cube構建完成后,如果某些歷史數據發生了變動,需要針對相應的Segment重新進行計算,這種構建稱為刷新。刷新通常只針對增量構建的Cube而言,因為全量構建的Cube只要重新全部構建就可以得到更新;而增量更新的Cube因為有多個Segment,需要先選擇刷新的Segment,然后再進行刷新。如下所示:
在刷新的同時,Cube仍可以被查詢,只是返回的是陳舊數據。在Segment刷新完畢后,新Segment會立即生效,查詢開始返回最新的數據。原Segment則成為垃圾,等待回收。
5. 查詢
Cube構建好后,即可進行查詢。不過只有當查詢的模式跟Cube定義相匹配的時候,Kylin才能夠使用Cube的數據來完成查詢。Group By 的列和 Where 條件里的列,必須是在維度中定義的列,而SQL中的度量,應該跟Cube中定義的度量一致。
例如,我們查詢每天的總銷售額的結果為:
可以看到耗時僅0.68s,速度非常快。得益於Kylin的預計算,可以大大縮減數據查詢的時間。