1. Apache Kylin 是什么?
Apache Kylin?是一個開源的分布式分析引擎,提供Hadoop之上的SQL查詢接口及多維分析(OLAP)能力以支持超大規模數據,最初由eBay Inc. 開發并貢獻至開源社區。它能在亞秒內查詢巨大的Hive表。
2. Apache Kylin框架介紹
Apache kylin 能提供低延遲(sub-second latency)的秘訣就是預計算,即針對一個星型拓撲結構的數據立方體,預計算多個維度組合的度量,然后將結果保存在hbase中,對外暴露JDBC、ODBC、Rest API的查詢接口,即可實現實時查詢。
如上圖所示,Kylin從Hadoop Hive中獲取數據,然后經過Cube Build Engine,將Hive中的數據Build成一個OLAP Cube保存在HBase中。用戶執行SQL查詢時,通過Query引擎,將SQL語句解析成OLAP Cube查詢,然后將結果返回給用戶。
3. Apache Kylin核心概念
1.表(table):This is definition of hive tables as source of cubes,在build cube 之前,必須同步在 kylin中。
2.模型(model):模型描述了一個星型模式的數據結構,它定義了一個事實表(Fact Table)和多個查找表(Lookup Table)的連接和過濾關系。
3. Cube 描述:描述一個Cube實例的定義和配置選項,包括使用了哪個數據模型、包含哪些維度和度量、如何將數據進行分區、如何處理自動合并等等。
4.Cube實例:通過Cube描述Build得到,包含一個或者多個Cube Segment。
5.分區(Partition):用戶可以在Cube描述中使用一個DATA/STRING的列作為分區的列,從而將一個Cube按照日期分割成多個segment。
6.立方體段(cube segmetn):它是立方體構建(build)后的數據載體,一個 segment 映射hbase中的一張表,立方體實例構建(build)后,會產生一個新的segment,一旦某個已經構建的立方體的原始數據發生變化,只需刷新(fresh)變化的時間段所關聯的segment即可。
7.聚合組:每一個聚合組是一個維度的子集,在內部通過組合構建cuboid。
8.作業(job):對立方體實例發出構建(build)請求后,會產生一個作業。該作業記錄了立方體實例build時的每一步任務信息。作業的狀態信息反映構建立方體實例的結果信息。如作業執行的狀態信息為RUNNING 時,表明立方體實例正在被構建;若作業狀態信息為FINISHED ,表明立方體實例構建成功;若作業狀態信息為ERROR ,表明立方體實例構建失??!
3.1 DIMENSION & MEASURE的種類
- Mandotary:強制維度,所有cuboid必須包含的維度。
- Hierarchy:層次關系維度,維度之間具有層次關系性,只需要保留一定層次關系的cuboid即可。
- Derived:衍生維度,在lookup 表中,有一些維度可以通過它的主鍵衍生得到,所以這些維度將不參加cuboid的構建。
- Count Distinct(HyperLogLog) :直接進行count distinct是很難去計算的,一個近似的算法HyperLogLog可以保持錯誤率在一個很低的范圍內。
- Count Distinct(Precise):將基于RoaringBitMap進行計算,目前只支持int和BigInt。
3.2 Cube Action種類
- BUILD:給定一個分區列指定的時間間隔,對Cube進行Build,創建一個新的cube Segment。
- REFRESH:這個操作,將在一些分期周期內對cube Segment進行重新build。
- MERGE:這個操作將合并多個cube segments。這個操作可以在構建cube時,設置為自動完成。
- PURGE:清理一個Cube實例下的segment,但是不會刪除HBase表中的Tables。
3.3 Job狀態
*NEW:表示一個job已經被創建。
*PENDING:表示一個job已經被job Scheduler提交,等待執行資源。
*RUNNING:表示一個job正在運行。
*FINISHED:表示一個job成功完成。
*ERROR:表示一個job因為錯誤退出。
*DISCARDED:表示一個job被用戶取消。
3.4 Job執行
*RESUME:這個操作將從失敗的Job的最后一個成功點繼續執行該Job。
*DISCARD:無論工作的狀態,用戶可以結束它和釋放資源。
4. Apache Kylin Cube 的構建過程
4.1 Cube的物理模型
如上圖所示,一個常用的3維立方體,包含:時間、地點、產品。假如data cell 中存放的是產量,則我們可以根據時間、地點、產品來確定產量,同時也可以根據時間、地點來確定所有產品的總產量等。
Apache Kylin就將所有(時間、地點、產品)的各種組合實現算出來,data cell 中存放度量,其中每一種組合都稱為cuboid。估n維的數據最多有2^n個cuboid,不過Kylin通過設定維度的種類,可以減少cuboid的數目。
4.2 Cube構建算法介紹
4.2.1 逐層算法(Layer Cubing)
我們知道,一個N維的Cube,是由1個N維子立方體、N個(N-1)維子立方體、N*(N-1)/2個(N-2)維子立方體、......、N個1維子立方體和1個0維子立方體構成,總共有2^N個子立方體組成,在逐層算法中,按維度數逐層減少來計算,每個層級的計算(除了第一層,它是從原始數據聚合而來),是基于它上一層級的結果來計算的。
比如,[Group by A, B]的結果,可以基于[Group by A, B, C]的結果,通過去掉C后聚合得來的;這樣可以減少重復計算;當 0維度Cuboid計算出來的時候,整個Cube的計算也就完成了。
如上圖所示,展示了一個4維的Cube構建過程。
此算法的Mapper和Reducer都比較簡單。Mapper以上一層Cuboid的結果(Key-Value對)作為輸入。由于Key是由各維度值拼接在一起,從其中找出要聚合的維度,去掉它的值成新的Key,并對Value進行操作,然后把新Key和Value輸出,進而Hadoop MapReduce對所有新Key進行排序、洗牌(shuffle)、再送到Reducer處;Reducer的輸入會是一組有相同Key的Value集合,對這些Value做聚合計算,再結合Key輸出就完成了一輪計算。
每一輪的計算都是一個MapReduce任務,且串行執行; 一個N維的Cube,至少需要N次MapReduce Job。
算法優點
- 此算法充分利用了MapReduce的能力,處理了中間復雜的排序和洗牌工作,故而算法代碼清晰簡單,易于維護;
- 受益于Hadoop的日趨成熟,此算法對集群要求低,運行穩定;在內部維護Kylin的過程中,很少遇到在這幾步出錯的情況;即便是在Hadoop集群比較繁忙的時候,任務也能完成。
算法缺點
- 當Cube有比較多維度的時候,所需要的MapReduce任務也相應增加;由于Hadoop的任務調度需要耗費額外資源,特別是集群較龐大的時候,反復遞交任務造成的額外開銷會相當可觀;
- 由于Mapper不做預聚合,此算法會對Hadoop MapReduce輸出較多數據; 雖然已經使用了Combiner來減少從Mapper端到Reducer端的數據傳輸,所有數據依然需要通過Hadoop MapReduce來排序和組合才能被聚合,無形之中增加了集群的壓力;
- 對HDFS的讀寫操作較多:由于每一層計算的輸出會用做下一層計算的輸入,這些Key-Value需要寫到HDFS上;當所有計算都完成后,Kylin還需要額外的一輪任務將這些文件轉成HBase的HFile格式,以導入到HBase中去;
- 總體而言,該算法的效率較低,尤其是當Cube維度數較大的時候;時常有用戶問,是否能改進Cube算法,縮短時間。
4.2.2 快速Cube算法(Fast Cubing)
快速Cube算法(Fast Cubing)是麒麟團隊對新算法的一個統稱,它還被稱作“逐段”(By Segment) 或“逐塊”(By Split) 算法。
該算法的主要思想是,對Mapper所分配的數據塊,將它計算成一個完整的小Cube 段(包含所有Cuboid);每個Mapper將計算完的Cube段輸出給Reducer做合并,生成大Cube,也就是最終結果;圖2解釋了此流程。
與舊算法相比,快速算法主要有兩點不同
- Mapper會利用內存做預聚合,算出所有組合;Mapper輸出的每個Key都是不同的,這樣會減少輸出到Hadoop MapReduce的數據量,Combiner也不再需要;
- 一輪MapReduce便會完成所有層次的計算,減少Hadoop任務的調配。
子立方體生成樹的遍歷
值得一提的還有一個改動,就是子立方體生成樹(Cuboid Spanning Tree)的遍歷次序;在舊算法中,Kylin按照層級,也就是廣度優先遍歷(Broad First Search)的次序計算出各個Cuboid;在快速Cube算法中,Mapper會按深度優先遍歷(Depth First Search)來計算各個Cuboid。深度優先遍歷是一個遞歸方法,將父Cuboid壓棧以計算子Cuboid,直到沒有子Cuboid需要計算時才出棧并輸出給Hadoop;最多需要暫存N個Cuboid,N是Cube維度數。
采用DFS,是為了兼顧CPU和內存:
- 從父Cuboid計算子Cuboid,避免重復計算;
-
只壓棧當前計算的Cuboid的父Cuboid,減少內存占用。
立方體生成數的遍歷過程
上圖是一個四維Cube的完整生成樹;按照DFS的次序,在0維Cuboid 輸出前的計算次序是 ABCD -> BCD -> CD -> D -> , ABCD, BCD, CD和D需要被暫存;在被輸出后,D可被輸出,內存得到釋放;在C被計算并輸出后,CD就可以被輸出; ABCD最后被輸出。
4.3 Cube構建流程
主要步驟如下:
- 構建一個中間平表(Hive Table):將Model中的fact表和look up表構建成一個大的Flat Hive Table。
- 重新分配Flat Hive Tables。
- 從事實表中抽取維度的Distinct值。
- 對所有維度表進行壓縮編碼,生成維度字典。
- 計算和統計所有的維度組合,并保存,其中,每一種維度組合,稱為一個Cuboid。
- 創建HTable。
- 構建最基礎的Cuboid數據。
- 利用算法構建N維到0維的Cuboid數據。
- 構建Cube。
- 將Cuboid數據轉換成HFile。
- 將HFile直接加載到HBase Table中。
- 更新Cube信息。
- 清理Hive。
5. Apache Kylin Cube 的存儲
簡單的說Cuboid的維度會映射為HBase的Rowkey,Cuboid的指標會映射為HBase的Value。
如上圖原始表所示:Hive表有兩個維度列year和city,有一個指標列price。如上圖預聚合表所示:我們具體要計算的是year和city這兩個維度所有維度組合(即4個cuboid)下的sum(priece)指標,這個指標的具體計算過程就是由MapReduce完成的。
如上圖字典編碼所示:為了節省存儲資源,Kylin對維度值進行了字典編碼。圖中將beijing和shanghai依次編碼為0和1。
如上圖HBase KV存儲所示:在計算cuboid過程中,會將Hive表的數據轉化為HBase的KV形式。Rowkey的具體格式是cuboid id + 具體的維度值(最新的Rowkey中為了并發查詢還加入了ShardKey),以預聚合表內容的第2行為例,其維度組合是(year,city),所以cuboid id就是00000011,cuboid是8位,具體維度值是1994和shanghai,所以編碼后的維度值對應上圖的字典編碼也是11,所以HBase的Rowkey就是0000001111,對應的HBase Value就是sum(priece)的具體值
6. Apache Kylin 如何將SQL轉換成HBase Scan查詢
還是以上面的例子進行解釋,假
設查詢SQL如下:
這個SQL涉及維度year和city,所以其對應的cuboid是00000011,又因為city的值是確定的beijing,所以在Scan HBase時就會Scan Rowkey以00000011開頭且city的值是beijing的行,取到對應指標sum(price)的值,返回給用戶。