Apache Kylin的實踐與優化

背景

銷售業務的特色是規模大、領域多、需求密。美團到店餐飲擎天銷售系統(如下簡稱「擎天」)做爲銷售數據支持的主要載體,不只涉及的範圍較廣,並且面臨的技術場景也很是複雜(多組織層級數據展現及鑑權、超過1/3的指標須要精準去重,峯值查詢已經達到數萬級別)。在這樣的業務背景下,建設穩定高效的OLAP引擎,協助分析人員快速決策,已經成爲到餐擎天的核心目標。html

Apache Kylin是一個基於Hadoop大數據平臺打造的開源OLAP引擎,它採用了多維立方體預計算技術,利用空間換時間的方法,將查詢速度提高至亞秒級別,極大地提升了數據分析的效率,並帶來了便捷、靈活的查詢功能。基於技術與業務匹配度,擎天於2016年採用Kylin做爲OLAP引擎,接下來的幾年裏,這套系統高效地支撐了咱們的數據分析體系。算法

2020年,美團到餐業務發展較快,數據指標也迅速增長。基於Kylin的這套系統,在構建和查詢上均出現了嚴重的效率問題,從而影響到數據的分析決策,並給用戶體驗優化帶來了很大的阻礙。技術團隊通過半年左右的時間,對Kylin進行一系列的優化迭代,包括維度裁剪、模型設計以及資源適配等等等,幫助銷售業績數據SLA從90%提高至99.99%。基於此次實戰,咱們沉澱了一套涵蓋了「原理解讀」、「過程拆解」、「實施路線」的技術方案。但願這些經驗與總結,可以幫助業界更多的技術團隊提升數據產出與業務決策的效率。apache

問題與目標

銷售做爲銜接平臺和商家的橋樑,包含銷售到店和電話拜訪兩種業務模式,以戰區、人力組織架構逐級管理,全部分析均須要按2套組織層級查看。在指標口徑一致、數據產出及時等要求下,咱們結合Kylin的預計算思想,進行了數據的架構設計。以下圖所示:緩存

而Kylin計算維度組合的公式是2^N(N爲維度個數),官方提供維度剪枝的方式,減小維度組合個數。但因爲到餐業務的特殊性,單任務不可裁剪的組合個數仍高達1000+。在需求迭代以及人力、戰區組織變更的場景下,須要回溯所有歷史數據,會耗費大量的資源以及超高的構建時長。而基於業務劃分的架構設計,雖可以極大地保證數據產出的解耦,保證指標口徑的一致性,可是對Kylin構建產生了很大的壓力,進而致使資源佔用大、耗時長。基於以上業務現狀,咱們概括了Kylin的MOLAP模式下存在的問題,具體以下:微信

  • 效率問題命中難(實現原理):構建過程步驟多,各步驟之間強關聯,僅從問題的表象很難發現問題的根本緣由,沒法行之有效地解決問題。
  • 構建引擎未迭代(構建過程):歷史任務仍採用MapReduce做爲構建引擎,沒有切換到構建效率更高的Spark。
  • 資源利用不合理(構建過程):資源浪費、資源等待,默認平臺動態資源適配方式,致使小任務申請了大量資源,數據切分不合理,產生了大量的小文件,從而形成資源浪費、大量任務等待。
  • 核心任務耗時長(實施路線):擎天銷售交易業績數據指標的源表數據量大、維度組合多、膨脹率高,致使天天構建的時長超過2個小時。
  • SLA質量不達標(實施路線):SLA的總體達成率未能達到預期目標。

在認真分析完問題,並肯定提效的大目標後,咱們對Kylin的構建過程進行了分類,拆解出在構建過程當中能提高效率的核心環節,經過「原理解讀」、「層層拆解」、「由點及面」的手段,達成雙向下降的目標。具體量化目標以下圖所示:架構

優化前提-原理解讀

爲了解決效率提高定位難、歸因難的問題,咱們解讀了Kylin構建原理,包含了預計算思想以及By-layer逐層算法。ide

預計算

根據維度組合出全部可能的維度,對多維分析可能用到的指標進行預計算,將計算好的結果保存成Cube。假設咱們有4個維度,這個Cube中每一個節點(稱做Cuboid)都是這4個維度的不一樣組合,每一個組合定義了一組分析的維度(如group by),指標的聚合結果就保存在每一個Cuboid上。查詢時,咱們根據SQL找到對應的Cuboid,讀取指標的值,便可返回。以下圖所示:工具

By-layer逐層算法

一個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的計算也就完成了。以下圖所示:oop

過程分析-層層拆解

在瞭解完Kylin的底層原理後,咱們將優化的方向鎖定在「引擎選擇」、「數據讀取」、「構建字典」、「分層構建」、「文件轉換」五個環節,再細化各階段的問題、思路及目標後,咱們終於作到了在下降計算資源的同時下降了耗時。詳情以下表所示:性能

構建引擎選擇

目前,咱們已經將構建引擎已逐步切換爲Spark。擎天早在2016年就使用Kylin做爲OLAP引擎,歷史任務沒有切換,僅僅針對MapReduce作了參數優化。其實在2017年,Kylin官網已啓用Spark做爲構建引擎(官網啓用Spark構建引擎),構建效率相較MapReduce提高1至3倍,還可經過Cube設計選擇切換,以下圖所示:

讀取源數據

Kylin之外部表的方式讀取Hive中的源數據,表中的數據文件(存儲在HDFS)做爲下一個子任務的輸入,此過程可能存在小文件問題。當前,Kylin上游數據寬表文件數分佈比較合理,無需在上游設置合併,若是強行合併反而會增長上游源表數據加工時間。

對於項目需求,要回刷歷史數據或增長維度組合,須要從新構建所有的數據,一般採用按月構建的方式回刷歷史,加載的分區過多出現小文件問題,致使此過程執行緩慢。在Kylin級別重寫配置文件,對小文件進行合併,減小Map數量,可有效地提高讀取效率。

合併源表小文件:合併Hive源表中小文件個數,控制每一個Job並行的Task個數。調整參數以下表所示:

Kylin級別參數重寫:設置Map讀取過程的文件大小。調整參數以下表所示:

構建字典

Kylin經過計算Hive表出現的維度值,建立維度字典,將維度值映射成編碼,並保存保存統計信息,節約HBase存儲資源。每一種維度組合,稱爲一個Cuboid。理論上來講,一個N維的Cube,便有2^N種維度組合。

組合數量查看

在對維度組合剪枝後,實際計算維度組合難以計算,可經過執行日誌(截圖爲提取事實表惟一列的步驟中,最後一個Reduce的日誌),查看具體的維度組合數量。以下圖所示:

全局字典依賴

擎天有不少業務場景須要精確去重,當存在多個全局字典列時,可設置列依賴,例如:當同時存在「門店數量」、「在線門店數量」數據指標,可設置列依賴,減小對超高基維度的計算。以下圖所示:

計算資源配置

當指標中存在多個精準去重指標時,可適當增長計算資源,提高對高基維度構建的效率。參數設置以下表所示:

分層構建

此過程爲Kylin構建的核心,切換Spark引擎後,默認只採用By-layer逐層算法,再也不自動選擇(By-layer逐層算法、快速算法)。Spark在實現By-layer逐層算法的過程當中,從最底層的Cuboid一層一層地向上計算,直到計算出最頂層的Cuboid(至關於執行了一個不帶group by的查詢),將各層的結果數據緩存到內存中,跳過每次數據的讀取過程,直接依賴上層的緩存數據,大大提升了執行效率。Spark執行過程具體內容以下。

Job階段

Job個數爲By-layer算法樹的層數,Spark將每層結果數據的輸出,做爲一個Job。以下圖所示:

Stage階段

每一個Job對應兩個Stage階段,分爲讀取上層緩存數據和緩存該層計算後的結果數據。以下圖所示:

Task並行度設置

Kylin根據預估每層構建Cuboid組合數據的大小(可經過維度剪枝的方式,減小維度組合的數量,下降Cuboid組合數據的大小,提高構建效率,本文暫不詳細介紹)和分割數據的參數值計算出任務並行度。計算公式以下:

  • Task個數計算公式:Min(MapSize/cut-mb ,MaxPartition) ;Max(MapSize/cut-mb ,MinPartition)

    • MapSize:每層構建的Cuboid組合大小,即:Kylin對各層級維度組合大小的預估值。
    • cut-mb:分割數據大小,控制Task任務並行個數,可經過kylin.engine.spark.rdd-partition-cut-mb參數設置。
    • MaxPartition:最大分區,可經過kylin.engine.spark.max-partition參數設置。
    • MinPartition:最小分區,可經過kylin.engine.spark.min-partition參數設置。
  • 輸出文件個數計算:每一個Task任務將執行完成後的結果數據壓縮,寫入HDFS,做爲文件轉換過程的輸入。文件個數即爲:Task任務輸出文件個數的彙總。

資源申請計算

平臺默認採用動態方式申請計算資源,單個Executor的計算能力包含:1個邏輯CPU(如下簡稱CPU)、6GB堆內內存、1GB的堆外內存。計算公式以下:

  • CPU = kylin.engine.spark-conf.spark.executor.cores * 實際申請的Executors個數。
  • 內存 =(kylin.engine.spark-conf.spark.executor.memory + spark.yarn.executor.memoryOverhead)* 實際申請的Executors個數。
  • 單個Executor的執行能力 = kylin.engine.spark-conf.spark.executor.memory / kylin.engine.spark-conf.spark.executor.cores,即:1個CPU執行過程當中申請的內存大小。
  • 最大Executors個數 = kylin.engine.spark-conf.spark.dynamicAllocation.maxExecutors,平臺默認動態申請,該參數限制最大申請個數。

在資源充足的狀況下,若單個Stage階段申請1000個並行任務,則須要申請資源達到7000GB內存和1000個CPU,即:CPU:1*1000=1000;內存:(6+1)*1000=7000GB

資源合理化適配

因爲By-layer逐層算法的特性,以及Spark在實際執行過程當中的壓縮機制,實際執行的Task任務加載的分區數據遠遠小於參數設置值,從而致使任務超高並行,佔用大量資源,同時產生大量的小文件,影響下游文件轉換過程。所以,合理的切分數據成爲優化的關鍵點。經過Kylin構建日誌,可查看各層級的Cuboid組合數據的預估大小,以及切分的分區個數(等於Stage階段實際生成的Task個數)。以下圖所示:

結合Spark UI可查看實執行狀況,調整內存的申請,知足執行所須要的資源便可,減小資源浪費。

1.總體資源申請最小值大於Stage階段Top一、Top2層級的緩存數據之和,保證緩存數據所有在內存。以下圖所示:

計算公式:Stage階段Top一、Top2層級的緩存數據之和 < kylin.engine.spark-conf.spark.executor.memory kylin.engine.spark-conf.spark.memory.fraction spark.memory.storageFraction *最大Executors個數

2.單個Task實際所須要的內存和CPU(1個Task執行使用1個CPU)小於單個Executor的執行能力。以下圖所示:

計算公式:單個Task實際所須要的內存 < kylin.engine.spark-conf.spark.executor.memory kylin.engine.spark-conf.spark.memory.fraction spark.memory.st·orageFraction / kylin.engine.spark-conf.spark.executor.cores。參數說明以下表所示:

文件轉換

Kylin將構建以後的Cuboid文件轉換成HTable格式的Hfile文件,經過BulkLoad的方式將文件和HTable進行關聯,大大下降了HBase的負載。此過程經過一個MapReduce任務完成,Map個數爲分層構建階段輸出文件個數。日誌以下:

此階段可根據實際輸入的數據文件大小(可經過MapReduce日誌查看),合理申請計算資源,避免資源浪費。

計算公式:Map階段資源申請 = kylin.job.mr.config.override.mapreduce.map.memory.mb * 分層構建階段輸出文件個數。具體參數以下表所示:

實施路線-由點及面

交易試點實踐

咱們經過對Kylin原理的解讀以及構建過程的層層拆解,選取銷售交易核心任務進行試點實踐。以下圖所示:

實踐結果對比

針對銷售交易核心任務進行實踐優化,對比調整先後資源實際使用狀況和執行時長,最終達到雙向下降的目標。以下圖所示:

成果展現

資源總體狀況

擎天現有20+的Kylin任務,通過半年時間持續優化迭代,對比Kylin資源隊列月均CU使用量和Pending任務CU使用量,在同等任務下資源消耗已明顯下降。以下圖所示:

SLA總體達成率

通過了由點及面的總體優化,擎天於2020年6月SLA達成率達到100%。以下圖所示:

展望

Apache Kylin在2015年11月正式成爲Apache基金會的頂級項目。從開源到成爲Apache頂級項目,只花了13個月的時間,並且它也是第一個由中國團隊完整貢獻到Apache的頂級項目。目前,美團採用比較穩定的V2.0版本,通過近4年的使用與積累,到餐技術團隊在優化查詢性能以及構建效率層面都積累了大量經驗,本文主要闡述了在Spark構建過程的資源適配方法。值得一提的是,Kylin官方在2020年7月發佈了V3.1版本,引入了Flink做爲構建引擎,統一使用Flink構建核心過程,包含數據讀取階段、構建字典階段、分層構建階段、文件轉換階段,以上四部分佔總體構建耗時的95%以上。這次版本的升級也大幅度提升了Kylin的構建效率。詳情可查看:Flink Cube Build Engine

回顧Kylin構建引擎的升級過程,從MapReduce到Spark,再到現在的Flink,構建工具的迭代始終向更加優秀的主流引擎在靠攏,並且Kylin社區有不少活躍的優秀代碼貢獻者,他們也在幫助擴大Kylin的生態,增長更多的新功能,很是值得你們學習。最後,美團到店餐飲技術團隊再次表達對Apache Kylin項目團隊的感謝。

做者簡介

嶽慶,2019年加入美團,到店餐飲研發中心工程師。

想閱讀更多技術文章,請關注美團技術團隊(meituantech)官方微信公衆號。

相關文章
相關標籤/搜索