Apache Kylin 從零開始構(gòu)建Cube(含優(yōu)化策略)

前言

Apache Kylin采用“預(yù)計算”的模式,用戶只需要提前定義好查詢維度,Kylin將幫助我們進(jìn)行計算,并將結(jié)果存儲到HBase中,為海量數(shù)據(jù)的查詢和分析提供亞秒級返回,是一種典型的“空間換時間”的解決方案。

Kylin架構(gòu)
  • Hadoop/Hive:Kylin是一個MOLAP系統(tǒng),將hive中的數(shù)據(jù)進(jìn)行預(yù)計算,利用MR或者SPARK來進(jìn)行實(shí)現(xiàn)
  • HBase:kylin用來存儲OLAP分析的cube數(shù)據(jù)的地方,實(shí)現(xiàn)多維數(shù)據(jù)集的交互式查詢
  • Rest Server:提供restful接口
  • Query Engine:使用開源的calcite框架實(shí)現(xiàn)sql的解析,是sql引擎層
  • Routing:負(fù)責(zé)將解析生成的執(zhí)行計劃轉(zhuǎn)換層cube緩存的查詢
  • Metadata:Kylin中大部分元數(shù)據(jù)信息的存儲
  • Cube Build Engine:負(fù)責(zé)kylin預(yù)計算中創(chuàng)建cube

一.概念

數(shù)據(jù)倉庫
Data Warehouse,簡稱DW,中文名數(shù)據(jù)倉庫,是商業(yè)智能(BI)中的核心部分。數(shù)據(jù)倉庫中存儲的則主要是歷史數(shù)據(jù),主要是將不同數(shù)據(jù)源的數(shù)據(jù)整合到一起,目的是為企業(yè)決策提供支持,所以可能存在大量數(shù)據(jù)冗余,但利于多個維度查詢,為決策者提供更多觀察視角。

OLAP
OLAP(Online Analytical Process),聯(lián)機(jī)分析處理,以多維度的方式分析數(shù)據(jù),一般帶有主觀的查詢需求,多應(yīng)用在數(shù)據(jù)倉庫,側(cè)重于提供決策支持。與之對應(yīng)的是OLTP(Online Transaction Process),聯(lián)機(jī)事務(wù)處理,側(cè)重于數(shù)據(jù)庫的增刪查改等常用業(yè)務(wù)操作。

OLAP以多維度的方式分析數(shù)據(jù),而且能夠彈性地提供以下幾種操作

  • 鉆取:在維的不同層次間的變化,從上層降到下一層,或者說將匯總數(shù)據(jù)拆分到更細(xì)節(jié)的數(shù)據(jù)
  • 上卷:鉆取的逆操作,即從細(xì)粒度數(shù)據(jù)向更高匯總層的聚合
  • 切片:選擇維中特定的值進(jìn)行分析
  • 切塊:選擇維中特定區(qū)間的數(shù)據(jù)或者某批特定值進(jìn)行分析
  • 旋轉(zhuǎn):維的位置互換,就像是二維表的行列轉(zhuǎn)換
OLAP操作.jpg

維度和度量

  • 維度是指審視數(shù)據(jù)的角度,它通常是數(shù)據(jù)記錄的一個屬性,例如時間、地點(diǎn)等。
  • 度量是基于數(shù)據(jù)所計算出來的考量值;它通常是一個數(shù)值,如總銷售額、不同的用戶數(shù)等。

事實(shí)表和維度表

  • 事實(shí)表(Fact Table)是指存儲有事實(shí)記錄的表,如系統(tǒng)日志、銷售記錄、傳感器數(shù)值等;
  • 維度表(Dimension Table)或維表,也叫做查找表(Lookup Table),是與事實(shí)表相對應(yīng)的一種表;它保存了維度的屬性值,可以跟事實(shí)表做關(guān)聯(lián);相當(dāng)于將事實(shí)表上經(jīng)常重復(fù)的屬性抽取、規(guī)范出來用一張表進(jìn)行管理。如日期表,地區(qū)表

模型概念

  • 星形模型:特點(diǎn)是只有一張事實(shí)表,以及零到多個維度表,事實(shí)表與維度表通過主外鍵相關(guān)聯(lián),維度表之間沒有關(guān)聯(lián);
  • 雪花模型:就是將星形模型中的某些維表抽取成更細(xì)粒度的維表,然后讓維表之間也進(jìn)行關(guān)聯(lián);
  • 星座模型:具有多個事實(shí)表,維表可以在不同事實(shí)表之間共用,這種模型被稱為星座模型;

二.構(gòu)建準(zhǔn)備

1.在Hive中準(zhǔn)備數(shù)據(jù)

需要被分析的數(shù)據(jù)必須先保存為Hive表的形式,然后Kylin才能從Hive中導(dǎo)入數(shù)據(jù),創(chuàng)建Cube。Cube支持從Hive視圖中構(gòu)建,基于這個特點(diǎn),可以將原始數(shù)據(jù)做一定的處理,如增加維度或者做一些預(yù)處理,生成相應(yīng)的視圖,基于視圖來構(gòu)建Cube。

2.維度表設(shè)計
  • 維度的基數(shù)不宜過大
  • 主鍵唯一
  • 維度表最好不是Hive的視圖

維度的基數(shù),維度的基數(shù)體現(xiàn)了Cube的復(fù)雜程度,維度基數(shù)過大,會增加Cube的膨脹程度,使用Count-Distinct來對一個維度的基數(shù)做一個統(tǒng)計,可以保證能夠設(shè)計合理的Cube。

Kylin支持增量Cube構(gòu)建,通常是按事件屬性來增量的從Hive表中抽取數(shù)據(jù)。因此Hive表最好按時間屬性分區(qū),這樣可以避免全量數(shù)據(jù)的掃描,減少讀寫操作對集群的壓力,節(jié)省Cube構(gòu)建的時間。

UHC 代表 Ultra High Cardinality,即超高基數(shù)?;鶖?shù)表示維度不同值的數(shù)量。通常,維度的基數(shù)從數(shù)十到數(shù)百萬。如果超過百萬,我們將其稱為超高基維度,Kylin 支持超高基維度,但是在 Cube 設(shè)計中額外注意超高基維度,它們可能會使 Cube 體積非常大、查詢變慢。

Cube 的最大物理維度數(shù)量 (不包括衍生維度) 是 63,但是不推薦使用大于 30 個維度的 Cube,會引起維度災(zāi)難。

三.設(shè)計Cube的全過程

1.導(dǎo)入Hive表

創(chuàng)建或者選擇一個已有的Project,將Hive中表的定義導(dǎo)入到Kylin中,Web界面的操作如下,Mode->DataSource->Load Hive Table。

LoadTable
導(dǎo)入Hive表

之后Kylin會觸發(fā)一個MR或者Spark任務(wù),計算此表基于每個列的基數(shù),這里Kylin對基數(shù)的計算方法采用的是HyperLogLog近似算法,與精確值有誤差,但是作為參考值已經(jīng)足夠了。

2.創(chuàng)建數(shù)據(jù)模型Data Model

數(shù)據(jù)模型是構(gòu)建Cube的基礎(chǔ),該數(shù)據(jù)模型可以描述為一個星型模型或者一個雪花模型,有了模型定義Cube的時候,可以在此模型定義的表和列中進(jìn)行選擇,基于一個模型可以創(chuàng)建多個Cube,減少了用戶的重復(fù)性工作。

在Web界面,點(diǎn)擊New->New Model,開始創(chuàng)建數(shù)據(jù)模型。

創(chuàng)建數(shù)據(jù)模型

填寫好基本信息之后,開始構(gòu)建模型。

首先選擇事實(shí)表,然后添加維度表,添加維度表需要選擇連接的類型,是Inner還是Left,然后選擇連接的主鍵和外鍵。


添加維度表

接下來會選擇用作維度或者度量的列,這里只是選擇一個范圍,不代表這些列將來一定會用作Cube的構(gòu)建,在這里可以把可能會用到的列都添加進(jìn)來,創(chuàng)建Cube的時候,將只能從這些列中選擇。

選擇維度

度量列只能來自事實(shí)表,維度列可以來自維度表和事實(shí)表。

最后一步是,為模型補(bǔ)充分割時間的列和過濾條件,如果此模型中的事實(shí)表的記錄是按照時間來增加的,可以指定一個日期或者時間列作為模型的分割時間列,從而可以讓Cube按此列做增量構(gòu)建。

除此之外,可以指定過濾條件。Kylin在向Hive請求數(shù)據(jù)的時候,會帶上此過濾條件。

3.設(shè)計Cube

1)首先選擇要使用的數(shù)據(jù)模型,并為此Cube輸入一個唯一的名稱,添加一些描述信息。

2)然后選擇Cube的維度

Add Dimension逐個添加維度,可以是普通維度,可以是衍生(Derived)維度。
需要為每一個維度起個名字,然后選擇表和列,如果是衍生維度,則必須是來自某個維度表,一次可以選擇多個列,這些列值都可以從該維度表的主鍵衍生出來。

添加維度

3)創(chuàng)建度量
Kylin默認(rèn)會創(chuàng)建一個Count(1)的度量。可以單擊“+Measure”按鈕來添加新的度量。Kylin支持的度量有:SUM、MIN、MAX、COUNT、COUNT DISTINCT、TOP_N、RAW等。Kylin可以支持在一個Cube中添加多達(dá)上百個的度量。

4)關(guān)于Cube數(shù)據(jù)刷新的設(shè)置。在這里可以設(shè)置自動合并的閾值、數(shù)據(jù)保留的最短時間,以及第一個Segment的起點(diǎn)時間(如果Cube有分割時間列的話)

Cube數(shù)據(jù)刷新的設(shè)置

設(shè)置Auto Merge Thresholds:合并的閾值可以設(shè)置多個層級,當(dāng)最大閾值不能滿足時,嘗試下一個稍小的閾值。

設(shè)置Volatile Range:如何你不想Kylin自動合并最近某個時間段的Segment,可以設(shè)置改屬性。

設(shè)置Retention Threshold:如果你想只保留最近1年的Segment中的數(shù)據(jù),可以設(shè)置該值為365。

5)高級設(shè)置。在此頁面上可以設(shè)置聚合組和Rowkey
Kylin默認(rèn)會把所有維度都放在同一個聚合組中;如果維度數(shù)較多(例如>10),那么建議用戶根據(jù)查詢的習(xí)慣和模式,單擊“New Aggregation Group+”,將維度分為多個聚合組。

設(shè)置聚合組

聚合組的優(yōu)化細(xì)節(jié)還會在本篇文章的后續(xù)講解。

在HBase中Key的存儲方式?
Kylin以Key-Value的方式將Cube存儲到HBase中。HBase的key,也就是Rowkey,是由各維度的值拼接而成的;為了更高效地存儲這些值,Kylin會對它們進(jìn)行編碼和壓縮;每個維度均可以選擇合適的編碼(Encoding)方式,默認(rèn)采用的是字典(Dictionary)編碼技術(shù);除了字典以外,還有整數(shù)(Int)和固定長度(Fixed Length)的編碼等。

字典編碼是將此維度下的所有值構(gòu)建成一個從string到int的映射表;Kylin會將字典序列化保存,在Cube中存儲int值,從而大大減小存儲的大小。

字典編碼的優(yōu)勢是產(chǎn)生的編碼非常緊湊,尤其在維度值的基數(shù)較小且長度較大的情況下,特別節(jié)約空間。由于產(chǎn)生的字典是在查詢時加載入構(gòu)建引擎和查詢引擎的,所以在維度的基數(shù)大、長度也大的情況下,容易造成構(gòu)建引擎或查詢引擎的內(nèi)存溢出。

Kylin支持的編碼方式還有以下幾種:

  • Date編碼:日期編碼,使用三個字節(jié)進(jìn)行編碼
  • Time編碼:Time編碼僅僅支持到秒,每個維度僅僅使用4個字節(jié)
  • Integer編碼:需要提供一個額外的參數(shù)“Length”來代表需要多少個字節(jié)
  • Fixed_length編碼:采用一段固定長度的字節(jié)來存儲代表維度值的字節(jié)數(shù)組。

各維度在Rowkeys中的順序如何設(shè)置?
各維度在Rowkeys中的順序,對于查詢的性能會產(chǎn)生較明顯的影響。通常建議將 mandantory 維度放在開頭, 然后是在過濾 ( where 條件)中起到很大作用的維度;如果多個列都會被用于過濾,將高基數(shù)的維度(如 user_id)放在低基數(shù)的維度(如 age)的前面。這樣做的好處是,充分利用過濾條件來縮小在HBase中掃描的范圍,從而提高查詢的效率。

其余需要主要的設(shè)置?

  • Mandatory Cuboids: 維度組合白名單。確保你想要構(gòu)建的 cuboid 能被構(gòu)建。
  • Cube Engine: cube 構(gòu)建引擎。有兩種:MapReduce 和 Spark。如果你的 cube 只有簡單度量(SUM, MIN, MAX),建議使用 Spark。如果 cube 中有復(fù)雜類型度量(COUNT DISTINCT, TOP_N),建議使用 MapReduce。
  • Advanced ColumnFamily: 如果有超過一個的COUNT DISTINCT 或 TopN 度量, 可以將它們放在更多列簇中,以優(yōu)化與HBase 的I/O。
Advanced ColumnFamily

6)高級設(shè)置
為Cube配置參數(shù)。和其他Hadoop工具一樣,Kylin使用了很多配置參數(shù)以提高靈活性,用戶可以根據(jù)具體的環(huán)境、場景等配置不同的參數(shù)進(jìn)行調(diào)優(yōu)。

單擊“+Property”按鈕,然后輸入?yún)?shù)名和參數(shù)值,如指定kylin.hbase.region.cut=2,這樣此Cube在存儲的時候,Kylin將會為每個HTable Region分配2GB來創(chuàng)建一個HTable Region。當(dāng)Segment中一些Cuboid的大小總和超出一定的閾值時,系統(tǒng)會將這些Cuboid的數(shù)據(jù)分片到多個分區(qū)中以實(shí)現(xiàn)Cuboid數(shù)據(jù)讀取的并行化,從而優(yōu)化Cube的查詢速度。

類型的配置還有:kylin.hbase.region.count.minkylin.hbase.region.count.max 決定每個Segment最少或最多被劃分成多少個分區(qū)。

最后再單擊“Save”按鈕進(jìn)行保存,一個Cube就設(shè)計完成了。

四.Cube的構(gòu)建

Cube的構(gòu)建方式通常有兩種:全量構(gòu)建和增量構(gòu)建;兩者的構(gòu)建步驟是完全一樣的,區(qū)別只在于構(gòu)建時讀取的數(shù)據(jù)源是全集還是子集

Cube的構(gòu)建是如何由任務(wù)引擎來調(diào)度執(zhí)行的?

  • 創(chuàng)建臨時的Hive平表(將數(shù)據(jù)從源Hive表提取出來,和所有join的表一起,并插入到一個中間平表)
  • 重新分配平面表(解決不平衡的文件分布導(dǎo)致之后的MR任務(wù)出現(xiàn)數(shù)據(jù)傾斜的問題)
  • 創(chuàng)建事實(shí)表的dictinct columns文件:計算每一個出現(xiàn)在事實(shí)表中的維度和度量的dictinct值
  • 構(gòu)建維度字典。
  • 保存Cuboid的統(tǒng)計數(shù)據(jù)
  • 創(chuàng)建HTable
  • 構(gòu)建Basic Cuboid
  • 構(gòu)建N維Cuboid
  • 基于內(nèi)存構(gòu)建Cube
  • 將Cube的計算結(jié)果轉(zhuǎn)成HFile
  • 加載HFile到HBase
  • 更新Cube元數(shù)據(jù)
  • 垃圾回收,清理構(gòu)建過程中生成的臨時文件等垃圾,釋放集群資源。

構(gòu)建N維Cuboid的詳細(xì)說明

構(gòu)建N維Cuboid的過程,每一步以前一步的輸出作為輸入,然后去掉一個維度以聚合得到一個子Cuboid。舉個例子,Cuboid ABCD去掉A得到BCD,去掉B得到ACD。

有些Cuboid可以從一個以上的父Cuboid聚合得到,這種情況下,Kylin會選擇最小的一個父Cuboid。舉例,AB可以從ABC(id:1110)和ABD(id:1101)生成,則ABD會被選中,因為它的比ABC要小。在這基礎(chǔ)上,如果D的基數(shù)較小,聚合運(yùn)算的成本就會比較低。所以,當(dāng)設(shè)計rowkey序列的時候,請記得將基數(shù)較小的維度放在末尾。這樣不僅有利于cube構(gòu)建,而且有助于cube查詢,因為預(yù)聚合也遵循相同的規(guī)則。

通常來說,從N維到(N/2)維的構(gòu)建比較慢,因為這是Cuboid數(shù)量爆炸性增長的階段:N維有1個cuboid,(N-1)維有N個cuboid,(N-2)維有N*(N-1)個cuboid,以此類推。經(jīng)過(N/2)維構(gòu)建的步驟,整個構(gòu)建任務(wù)會逐漸變快。

五.Cube的剪枝優(yōu)化

一般來說,Cube的膨脹率應(yīng)該在0%~1000%之間,通常,膨脹率高有以下幾個方面的原因:

  • Cube中的維度數(shù)量較多,且沒有進(jìn)行很好的Cuboid剪枝優(yōu)化,導(dǎo)致Cuboid數(shù)量極多
  • Cube中存在較高基數(shù)的維度,導(dǎo)致包含這類維度的每一個Cuboid占用的空間都很大,這些Cuboid累積造成整體Cube體積變大。
  • 存在比較占用空間的度量,如Count Distinct,因此需要在Cuboid的每一行中都為其保存一個較大的寄存器

剪枝優(yōu)化策略如下所示:

1.使用衍生維度

衍生維度用于在有效維度內(nèi)將維度表上的非主鍵維度排除掉,并使用維度表的主鍵(其實(shí)是事實(shí)表上相應(yīng)的外鍵)來替代它們。Kylin會在底層記錄維度表主鍵與維度表其他維度之間的映射關(guān)系,以便在查詢時能夠動
態(tài)地將維度表的主鍵“翻譯”成這些非主鍵維度,并進(jìn)行實(shí)時聚合。

例如:
原始組合:ABC,AB,AC,BC,A,B,C
當(dāng)定義B維度從A維度衍生時的組合:AC,A,C

可見從7種組合變成了3種組合。

假設(shè)原始的維度表這樣定義

A B C
1 a ?
2 b ?
3 c ?
4 b ?

如果我們想根據(jù)B維度來進(jìn)行查詢,如select count(*) from tbl inner join lookup_tbl group by lookup_tbl.B
則Kylin會對該查詢進(jìn)行優(yōu)化,使其由A維度進(jìn)行分組。

A COUNT(*)
1 1
2 1
3 1
4 1

之后根據(jù)B維度從A維度衍生出來的映射關(guān)系,將A替換為B,則如下所示

B COUNT(*)
a 1
b 1
c 1
b 1

最后會對以上結(jié)果進(jìn)行聚合操作,如下所示

B COUNT(*)
a 1
b 2
c 1

因此,如果從維度表主鍵到某個維度表維度所需要的聚合工作量非常大,那么定義一個普通的維度可能是一種更好的選擇。

2.使用聚合組

聚合組假設(shè)一個Cube的所有維度均可以根據(jù)業(yè)務(wù)需求劃分成若干組。每個分組的維度集合均是Cube所有維度的一個子集,不同的分組各自擁有一套維度集合,它們可能與其他分組有相同的維度,也可能沒有相同的維度。構(gòu)建引擎會保證每一個Cuboid無論在多少個分組中出現(xiàn),它都只會被物化一次。

通過使用多個聚合組,可以大大降低Cube中的Cuboid數(shù)量。下面來舉例說明,如果一個Cube有(M+N)個維度,那么默認(rèn)它會有2^(m + n) 個 Cuboid;如果把這些維度分為兩個不相交的聚合組,那么Cuboid的數(shù)量將被減少為2^m + 2^n。

在單個聚合組中,可以對維度設(shè)置高級屬性,如Mandatory、Hierarchy、Joint等。這幾種屬性都是為優(yōu)化Cube的計算而設(shè)計的。

Mandatory 必要維度,總是出現(xiàn)的維度。指的是那些總是會出現(xiàn)在Where條件或Group By語句里的維度;通過將某個維度指定為Mandatory,此聚合組產(chǎn)生的所有Cuboid中每一個Cuboid都會包含該維度,Kylin就可以不用預(yù)計算那些不包含此維度的Cuboid,從而減少計算量。

Hierarchy 層級維度,例如 “國家” -> “省” -> “市” 是一個層級;不符合此層級關(guān)系的 cuboid 可以被跳過計算,例如 [“省”], [“市”]. 定義層級維度時,將父級別維度放在子維度的左邊。通過指定Hierarchy,Kylin可以省略不滿足此模式的Cuboid。假設(shè)一個層級中包含D1,D2…Dn這n個維度,那么在該分組產(chǎn)生的任何Cuboid中,這n個維度只會以(),(D1),(D1,D2)…(D1,D2…Dn)這n+1種形式中的一種出現(xiàn)。

Joint 聯(lián)合維度,其通常適用于如下兩種情形??偸菚谝黄鸩樵兊木S度,基數(shù)非常接近(有1:1映射關(guān)系)。如果某些列形成一個聯(lián)合,那么在該分組產(chǎn)生的任何Cuboid中,這些聯(lián)合維度要么一起出現(xiàn),要么都不出現(xiàn)。

高基數(shù)維度使用聚合組控制Cube的膨脹率的思想?
高基數(shù)維度的Cuboid在行數(shù)和體積上往往非常龐大,這會導(dǎo)致整個Cube的膨脹率變大。如果根據(jù)業(yè)務(wù)需求知道這個高基數(shù)的維度只會與若干個維度(而不是所有維度)同時被查詢到,那么就可以通過聚合組對這個高基數(shù)維度做一定的“隔離”。

把這個高基數(shù)的維度放入一個單獨(dú)的聚合組,再把所有可能會與這個高基數(shù)維度一起被查詢到的其他維度也放進(jìn)來。

這樣,這個高基數(shù)的維度就被“隔離”在一個聚合組中了,所有不會與它一起被查詢到的維度都沒有和它一起出現(xiàn)在任何一個分組中,因此也就不會有多余的Cuboid產(chǎn)生。這點(diǎn)也大大減少了包含該高基數(shù)維度的Cuboid的數(shù)量,可以有效地控制Cube的膨脹率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容