天天看點

Apache Kylin權威指南3.4 管理Cube碎片

<b>3.4 管理cube碎片</b>

<b></b>

增量建構的cube每天都可能會有新的增量。日積月累,這樣的cube中最終可能包含上百個segment,這将會導緻查詢性能受到嚴重的影響,因為運作時的查詢引擎需要聚合多個segment的結果才能傳回正确的查詢結果。從存儲引擎的角度來說,大量的segment會帶來大量的檔案,這些檔案會充斥所提供的命名空間,給存儲空間的多個子產品帶來巨大的壓力,例如zookeeper、hdfs namenode等。是以,有必要采取措施控制cube中segment的數量。

另外,有時候使用者場景并不能完美地符合增量建構的要求,由于etl過程存在延遲,資料可能一直在持續地更新,有時候使用者不得不在增量更新已經完成後又回過頭來重新整理過去已經建構好了的增量segment,對于這些問題,需要在設計cube的時候提前進行考慮。

3.4.1 合并segment

kylin提供了一種簡單的機制用于控制cube中segment的數量:合并segments。在web gui中選中需要進行segments合并的cube,單擊action→merge,然後在對話框中選中需要合并的segment,可以同時合并多個segment,但是這些segment必須是連續的。單擊送出後系統會送出一個類型為“merge”的建構任務,它以選中的segment中的資料作為輸入,将這些segment的資料合并封裝成為一個新的segment(如圖3-5所示)。這個新的segment的起始時間為選中的最早的segment的起始時間,它的結束時間為選中的最晚的segment的結束時間。

圖3-5 合并segment

在merge類型的建構完成之前,系統将不允許送出這個cube上任何類型的其他建構任務。但是在merge建構結束之前,所有選中用來合并的segment仍然處于可用的狀态。當merge建構結束的時候,系統将選中合并的segment替換為新的segment,而被替換下的segment等待将被垃圾回收和清理,以節省系統資源。

使用者也可以使用rest接口觸發合并segments,該api在之前的觸發增量建構中也已經提到過:

put http://hostname:port/kylin/api/cubes/{cubename}/rebuild

path variable

cubename – 必須的,cube名字

request body

starttime – 必須的,長整數類型的起始時間,例如使用1388563200000代表起始時間為2014-01-01

endtime – 必須的,長整數類型的結束時間

buildtype – 必須的,建構類型,可能的值為‘build’‘merge’和‘refresh’,分别對應于建立segment、合并多個segment,以及重新整理某個segment

我們需要将buildtype設定為merge,并且将starttime設定為選中的需要合并的最早的segment的起始時間,将endtime設定為選中的需要合并的最晚的segment的結束時間。

合并segment非常簡單,但是需要cube管理者不定期地手動觸發合并,尤其是當生産環境中存在大量的cube時,對每一個cube單獨觸發合并操作會變得非常繁瑣,是以,kylin也提供了其他的方式來管理segment碎片。

3.4.2 自動合并

在3.2.2節中曾提到過,在cube designer的“refresh settings”的頁面中有“auto merge thresholds”和“retention threshold”兩個設定項可以用來幫助管理segment碎片。雖然這兩項設定還不能完美地解決所有業務場景的需求,但是靈活地搭配使用這兩項設定可以大大減少對segment進行管理的麻煩。

“auto merge thresholds”允許使用者設定幾個層級的時間門檻值,層級越靠後,時間門檻值就越大。舉例來說,使用者可以為一個cube指定(7天、28天)這樣的層級。每當cube中有新的segment狀态變為ready的時候,就會觸發一次系統試圖自動合并的嘗試。系統首先會嘗試最大一級的時間門檻值,結合上面的(7天、28天)層級的例子,首先檢視是否能将連續的若幹個segment合并成為一個超過28天的大segment,在挑選連續segment的過程中,如果遇到已經有個别segment的時間長度本身已經超過了28天,那麼系統會跳過該segment,從它之後的所有segment中挑選連續的累積超過28天的segment。如果滿足條件的連續segment還不能夠累積超過28天,那麼系統會使用下一個層級的時間門檻值重複尋找的過程。每當找到了能夠滿足條件的連續segment,系統就會觸發一次自動合并segment的建構任務,在建構任務完成之後,新的segment被設定為ready狀态,自動合并的整套嘗試又需要重新再來一遍。

舉例來說,如果現在有a~h 8個連續的segment,它們的時間長度分别為28天(a)、7天(b)、1天(c)、1天(d)、1天(e)、1天(f)、1天(g)、1天(h)。此時第9個segment i加入,它的時間長度為1天,那麼現在cube中總共存在9個segment。系統首先嘗試能否将連續的segment合并到28天這個門檻值上,由于segment a已經超過28天,它會被排除。接下來的b到h加起來也不足28天,是以第一級的時間門檻值無法滿足,退一步系統嘗試第二級的時間門檻值,也就是7天。系統重新掃描所有的segment,發現a和b已經超過7天,是以跳過它們,接下來發現将segment c到i合并起來可以達到7天的門檻值,是以系統會送出一個合并segment的建構請求,将segment c到i合并為一個新的segment x。x的建構完成之後,cube中隻剩下三個segment,分别是原來的a(28天),b(7天)和新的x(7天)。由于x的加入,觸發了系統重新開始整個合并嘗試,但是發現已經沒有滿足自動合并的條件,既沒有連續的、滿足條件的、累積超過28天的segment,也沒有連續的、滿足條件的、累積超過7天的segment,嘗試終止。

再舉一個例子,如果現在有a~j 10個連續的segment,它們的時間長度分别為28天(a)、7天(b)、7天(c)、7天(d)、1天(e)、1天(f)、1天(g)、1天(h)、1天(i)、1天(j)。此時第11個segment k加入,它的時間長度為1天,那麼現在cube中總共存在11個segment。系統首先嘗試能否将連續的segment合并到28天這個門檻值上,由于segment a已經超過28天,它會被排除。系統接着從segment b開始觀察,發現若把segment b至k這10個連續的segment合并在一起正好可以達到第一級的門檻值28天,是以系統送出一個合并建構任務把b至k合并為一個新的segment x,最終cube中存在兩個長度均為28天的segment,依次對應原來的a和新的x。由于x的加入,觸發了系統重新開始整個合并嘗試,但是發現已經沒有滿足自動合并的條件,嘗試終止。

“auto merge thresholds”的設定非常簡單,在cube designer的“refresh setting”中,單擊“auto merge thresholds”右側的“new thresholds”按鈕,即可在層級的時間門檻值中添加一個新的層級,層級一般按照升序進行排列(如圖3-6所示)。從前面的介紹中不難得出結論,除非人為地增量建構一個非常大的segment,自動合并的cube中,最大的segment的時間長度等于層級時間門檻值中最大的層級。也就是說,如果層級被設定為(7天、28天),那麼cube中最長的segment也不過是28天,不會出現橫跨半年甚至一年的大segment。

在一些場景中,使用者可能更希望系統能以自然日的星期、月、年作為機關進行自動合并,這樣在隻需要查詢個别月份的資料時,就能夠隻通路該月的segment,而非兩個毗鄰的28天長度的segment。對此,https://issues.apache.org/jira/browse/kylin-1865 記錄了這個問題。

圖3-6 設定自動合并門檻值

3.4.3 保留segment

從碎片管理的角度來說,自動合并是将多個segment合并為一個segment,以達到清理碎片的目的。保留segment則是從另外一個角度幫助實作碎片管理,那就是及時清理不再使用的segment。在很多業務場景中,隻會對過去一段時間内的資料進行查詢,例如對于某個隻顯示過去1年資料的報表,支撐它的cube事實上隻需要保留過去一年内的segment即可。由于資料在hive中往往已經存在備份,是以無需再在kylin中備份超過一年的曆史資料。

在這種情況下,我們可以将“retention threshold”設定為365。每當有新的segment狀态變為ready的時候,系統會檢查每一個segment:如果它的結束時間距離最晚的一個segment的結束時間已經大于“retention threshold”,那麼這個segment将被視為無需保留。系統會自動地從cube中删除這個segment。

如果啟用了“auto merge thresholds”,那麼在使用“retention threshold”的時候需要注意,不能将“auto merge thresholds”的最大層級設定得太高。假設我們将“auto merge thresholds”的最大一級設定為1000天,而将“retention threshold”設定為365天,那麼受到自動合并的影響,新加入的segment會不斷地被自動合并到一個越來越大的segment之中,糟糕的是,這會不斷地更新這個大segment的結束時間,進而導緻這個大segment永遠不會得到釋放。是以,推薦自動合并的最大一級的時間不要超過1年。

3.4.4 資料持續更新

在實際應用場景中,我們常常會遇到這樣的問題:由于etl過程的延遲,業務每天都需要重新整理過去n天的cube資料。舉例來說,客戶有一個報表每天都需要更新,但是每天的源資料更新不僅包含了當天的新資料,還包括了過去7天内資料的補充。一種比較簡單的方法是,每天在cube中增量建構一個長度為一天的segment,這樣過去7天的資料就會以7個segment的形式存在于cube之中。cube的管理者除了每天要建立一個新的segment代表當天的新資料(build操作)以外,還需要對代表過去7天的7個segment進行重新整理(refresh操作,web gui上的操作及rest api參數與build類似,這裡不再詳細展開)。這樣的方法固然可以奏效,但是每天為每個cube觸發的建構數量太多,容易造成kylin的任務隊列堆積大量未能完成的任務。

上述簡單方案的另外一個弊端是,每天一個segment也會讓cube中迅速地累積大量的segment,需要cube管理者手動地對曆史已經超過7天的segment進行合并,期間還必須小心翼翼地,不能錯将7天内的segment一起合并了。舉例來說,假設現在有100個segment,每個segment代表過去的一天的資料,segment按照起始時間排序。在合并時,我們隻能挑選前面93個segment進行合并,如果不小心把第94個segment也一起合并了,那麼當我們試圖重新整理過去7天(94~100)的segment的時候,會發現為了重新整理第94天的資料,不得不将1~93的資料一并重新計算,因為此時第94天的資料已經和1~93這93天的資料糅合在一個segment之中了。這對于重新整理來說是一種極大的浪費。糟糕的是,即使使用之前所介紹的自動合并的功能,類似的問題也仍然存在,目前為止,還沒有一種機制能夠有效阻止自動合并試圖合并近期n天的segment,是以使用自動合并仍然有可能将最近n天内的某些segment與更早的其他segment合并成一個大的segment,這個問題将在https://issues.apache.org/jira/browse/kylin-1864中獲得解決。

目前來說,比較折中的一種方案是不以日為機關建立新的segment,而是以n天為機關建立新的segment。舉例來說,假設使用者每天更新cube的時候,前面7天的資料都需要更新一下,也就是說,如果今天是01-08,那麼使用者不僅要添加01-08的新資料,還要同時更新01-01到01-07的資料。在這種情況下,可設定n=7作為最小segment的長度。在第一天01-01,建立一個新的segment a,它的時間是從01-01到01-08,我們知道segment是起始時間閉,結束時間開,是以segment a的真實長度為7天,也就是01-01到01-07。即使在01-01當天,還沒有後面幾天的資料,segment a也能正常地建構,隻不過建構出來的segment其實隻有01-01一天的資料而已。從01-02到01-07的每一天,我們都要重新整理segment a,以保證1日到7日的資料保持更新。由于01-01已經是最早的日期,是以不需要對更早的資料進行更新。

到01-08的時候,建立一個新的segment b,它的時間是從01-08到01-15。此時我們不僅需要建構segment b,還需要去重新整理segment a。因為01-01到01-07中的資料在01-08當天仍然可能處于更新狀态。在接下來的01-09到01-14,每天重新整理a、b兩個segment。等到了01-15這天的時候,首先建立一個新的segment c,它的時間是從01-15到01-22。在01-15當天,segment a的資料應當已經被視作最終狀态,因為segment a中的最後一天(01-07)已經不再過去n天的範圍之内了。是以此時接下來隻需要照顧segment b和segment c即可。

由此可以看到,在任意一天内,我們隻需要同時照顧兩個segment,第一個segment主要以重新整理近期資料為主,第二個segment則兼顧了加入新資料與重新整理近期資料。這個過程中可能存在少量的多餘計算,但是每天多餘計算的資料量不會超過n天的資料量。這對于kylin整體的計算量來說是可以接受的。根據業務場景的不同,n可能是7天,也有可能是30天,我們可以适度地把最小的segment設定成比n稍微大一點的數字,例如n為7的時候,我們可以設定為10天,這樣即使etl有時候沒有能夠遵守n=7的約定,也仍然能夠重新整理足夠的資料。值得一提的是,在https://issues.apache.org/jira/browse/kylin-1864得到解決之前,我們不要重疊使用自動合并和本節中所描述的處理資料陸續更新的政策。