在使用Kylin的時候,最重要的一步就是建立cube的模型定義,即指定度量和維度以及一些附加信息,而後對cube進行build,固然咱們也能夠根據原始表中的某一個string字段(這個字段的格式必須是日期格式,表示日期的含義)設定分區字段,這樣一個cube就能夠進行屢次build,每一次的build會生成一個segment,每個segment對應着一個時間區間的cube,這些segment的時間區間是連續而且不重合的,對於擁有多個segment的cube能夠執行merge,至關於將一個時間區間內部的segment合併成一個。下面開始分析cube的build過程。web
以手機銷售爲例,表SALE記錄各手機品牌在各個國家,每一年的銷售狀況。表PHONE是手機品牌,表COUNTRY是國家列表,兩表經過外鍵與SALE表相關聯。這三張表就構成星型模型,其中SALE是事實表,PHONE、COUNTRY是維度表。redis
如今須要知道各品牌手機於2010-2012年,在中國的總銷量,那麼查詢sql爲:sql
SELECT b.`name`, c.`NAME`, SUM(a.count) FROM SALE AS a LEFT JOIN PHONE AS b ON a.`pId`=b.`id` LEFT JOIN COUNTRY AS c ON a.`cId`=c.`id` WHERE a.`time` >= 2010 AND a.`time` <= 2012 AND c.`NAME` = "中國" GROUP BY b.`NAME`
其中時間(time), 手機品牌(b.name,後文用phone代替),國家(c.name,後文用country代替)是維度,而銷售數量(a.count)是度量。手機品牌的個數可用於表示手機品牌列的基度。各手機品牌在各年各個國家的銷量可做爲一個cuboid,全部的cuboid組成一個cube,以下圖所示:
shell
上圖展現了有3個維度的cube,每一個小立方體表明一個cuboid,其中存儲的是度量列聚合後的結果,好比蘋果在中國2010年的銷量就是一個cuboid。數據庫
在kylin的web頁面上建立完成一個cube以後能夠點擊action下拉框執行build或者merge操做,這兩個操做都會調用cube的rebuild接口,調用的參數包括:app
一、cube名,用於惟一標識一個cube,在當前的kylin版本中cube名是全局惟一的,而不是每個project下惟一的;
二、本次構建的startTime和endTime,這兩個時間區間標識本次構建的segment的數據源只選擇這個時間範圍內的數據;對於BUILD操做而言,startTime是不須要的,由於它老是會選擇最後一個segment的結束時間做爲當前segment的起始時間。
三、buildType標識着操做的類型,能夠是」BUILD」、」MERGE」和」REFRESH」。函數
Kylin中Cube的Build過程,是將全部的維度組合事先計算,存儲於HBase中,以空間換時間,HTable對應的RowKey,就是各類維度組合,指標存在Column中,這樣,將不一樣維度組合查詢SQL,轉換成基於RowKey的範圍掃描,而後對指標進行彙總計算,以實現快速分析查詢。整個過程以下圖所示:
主要的步驟能夠按照順序分爲幾個階段:
一、根據用戶的cube信息計算出多個cuboid文件;
二、根據cuboid文件生成htable;
三、更新cube信息;
四、回收臨時文件。
每個階段操做的輸入都須要依賴於上一步的輸出,因此這些操做全是順序執行的。下面對這幾個階段的內容細分爲11步具體講解一下:工具
這一步的操做會新建立一個hive外部表,而後再根據cube中定義的星狀模型,查詢出維度和度量的值插入到新建立的表中,這個表是一個外部表,表的數據文件(存儲在HDFS)做爲下一個子任務的輸入。性能
在前面步驟,hive會在HDFS文件夾中生成數據文件,一些文件很是大,一些有些小,甚至是空的。文件分佈不平衡會致使隨後的MR做業不平衡:一些mappers做業很快執行完畢,但其它的則很是緩慢。爲了平衡做業,kylin增長這一步「從新分配」數據。首先,kylin獲取到這中間表的行數,而後根據行數的數量,它會從新分配文件須要的數據量。默認狀況下,kylin分配每100萬行一個文件。ui
在這一步是根據上一步生成的hive中間表計算出每個出如今事實表中的維度列的distinct值,並寫入到文件中,它是啓動一個MR任務完成的,它關聯的表就是上一步建立的臨時表,若是某一個維度列的distinct值比較大,那麼可能致使MR任務執行過程當中的OOM。
這一步是根據上一步生成的distinct column文件和維度表計算出全部維度的子典信息,並以字典樹的方式壓縮編碼,生成維度字典,子典是爲了節約存儲而設計的。
每個cuboid的成員是一個key-value形式存儲在hbase中,key是維度成員的組合,可是通常狀況下維度是一些字符串之類的值(例如商品名),因此能夠經過將每個維度值轉換成惟一整數而減小內存佔用,在從hbase查找出對應的key以後再根據子典獲取真正的成員值。
計算和統計全部的維度組合,並保存,其中,每一種維度組合,稱爲一個Cuboid。理論上來講,一個N維的Cube,便有2的N次方種維度組合,參考網上的一個例子,一個Cube包含time, item, location, supplier四個維度,那麼組合(Cuboid)便有16種:
建立一個HTable的時候還須要考慮一下幾個事情:
一、列簇的設置。
二、每個列簇的壓縮方式。
三、部署coprocessor。
四、HTable中每個region的大小。
在這一步中,列簇的設置是根據用戶建立cube時候設置的,在HBase中存儲的數據key是維度成員的組合,value是對應聚合函數的結果,列簇針對的是value的,通常狀況下在建立cube的時候只會設置一個列簇,該列包含全部的聚合函數的結果;
在建立HTable時默認使用LZO壓縮,若是不支持LZO則不進行壓縮,在後面kylin的版本中支持更多的壓縮方式;
kylin強依賴於HBase的coprocessor,因此須要在建立HTable爲該表部署coprocessor,這個文件會首先上傳到HBase所在的HDFS上,而後在表的元信息中關聯,這一步很容易出現錯誤,例如coprocessor找不到了就會致使整個regionServer沒法啓動,因此須要特別當心;region的劃分已經在上一步肯定了,因此這裏不存在動態擴展的狀況,因此kylin建立HTable使用的接口以下:
public void createTable(final HTableDescriptor desc , byte [][] splitKeys)
在Kylin的Cube模型中,每個cube是由多個cuboid組成的,理論上有N個普通維度的cube能夠是由2的N次方個cuboid組成的,那麼咱們能夠計算出最底層的cuboid,也就是包含所有維度的cuboid(至關於執行一個group by所有維度列的查詢),而後在根據最底層的cuboid一層一層的向上計算,直到計算出最頂層的cuboid(至關於執行了一個不帶group by的查詢),其實這個階段kylin的執行原理就是這個樣子的,不過它須要將這些抽象成mapreduce模型,提交Spark做業執行。
使用Spark,生成每一種維度組合(Cuboid)的數據。
Build Base Cuboid Data;
Build N-Dimension Cuboid Data : 7-Dimension;
Build N-Dimension Cuboid Data : 6-Dimension;
……
Build N-Dimension Cuboid Data : 2-Dimension;
Build Cube。
建立完了HTable以後通常會經過插入接口將數據插入到表中,可是因爲cuboid中的數據量巨大,頻繁的插入會對Hbase的性能有很是大的影響,因此kylin採起了首先將cuboid文件轉換成HTable格式的Hfile文件,而後在經過bulkLoad的方式將文件和HTable進行關聯,這樣能夠大大下降Hbase的負載,這個過程經過一個MR任務完成。
將HFile文件load到HTable中,這一步徹底依賴於HBase的工具。這一步完成以後,數據已經存儲到HBase中了,key的格式由cuboid編號+每個成員在字典樹的id組成,value可能保存在多個列組裏,包含在原始數據中按照這幾個成員進行GROUP BY計算出的度量的值。
更新cube的狀態,其中須要更新的包括cube是否可用、以及本次構建的數據統計,包括構建完成的時間,輸入的record數目,輸入數據的大小,保存到Hbase中數據的大小等,並將這些信息持久到元數據庫中。
這一步是否成功對正確性不會有任何影響,由於通過上一步以後這個segment就能夠在這個cube中被查找到了,可是在整個執行過程當中產生了不少的垃圾文件,其中包括:
一、臨時的hive表;
二、由於hive表是一個外部表,存儲該表的文件也須要額外刪除;
三、fact distinct這一步將數據寫入到HDFS上爲創建子典作準備,這時候也能夠刪除了;
四、rowKey統計的時候會生成一個文件,此時能夠刪除;
五、生成HFile時文件存儲的路徑和hbase真正存儲的路徑不一樣,雖然load是一個remove操做,可是上層的目錄仍是存在的,也須要刪除。
至此整個Build過程結束。