Apache Kylin VS Apache Doris

作者: 康凱森

日期: 2018-04-17

分類: OLAP


Apache Kylin 和 Apache Doris 都是優秀的開源OLAP系統,本文將全方位地對比Kylin和Doris。Kylin和Doris分別是MOALP和ROLAP的代表,對比這兩個系統的目的不是爲了說明哪個系統更好,只是爲了明確每個系統的設計思想和架構原理,讓大家可以根據自己的實際需求去選擇合適的系統,也可以進一步去思考我們如何去設計出更優秀的OLAP系統

本文對Apache Kylin的理解基於近兩年來在生產環境大規模地使用,運維和深度開發,我已向Kylin社區貢獻了98次Commit,包含多項新功能和深度優化。

本文對Apache Doris的理解基於官方文檔和論文的閱讀,代碼的粗淺閱讀和較深入地測試。

注: 本文的對比基於Apache Kylin 2.0.0 和Apache Doris 0.9.0。

1 系統架構

1.1 What is Kylin

Kylin的核心思想是預計算利用空間換時間來加速查詢模式固定的OLAP查詢

Kylin的理論基礎是Cube理論,每一種維度組合稱之爲Cuboid,所有Cuboid的集合是Cube。 其中由所有維度組成的Cuboid稱爲Base Cuboid,圖中(A,B,C,D)即爲Base Cuboid,所有的Cuboid都可以基於Base Cuboid計算出來。 在查詢時,Kylin會自動選擇滿足條件的最“小”Cuboid,比如下面的SQL就會對應Cuboid(A,B):

select xx from table where A=xx group by B

Kylin-cube

下圖是Kylin數據流轉的示意圖,Kylin自身的組件只有兩個:JobServer和QueryServer。 Kylin的JobServer主要負責將數據源(Hive,Kafka)的數據通過計算引擎(MapReduce,Spark)生成Cube存儲到存儲引擎(HBase)中;QueryServer主要負責SQL的解析,邏輯計劃的生成和優化,向HBase的多個Region發起請求,並對多個Region的結果進行彙總,生成最終的結果集。 kylin-data

下圖是Kylin可插拔的架構圖, 在架構設計上,Kylin的數據源,構建Cube的計算引擎存儲引擎都是可插拔的。Kylin的核心就是這套可插拔架構,Cube數據模型和Cuboid的算法。

Kylin

1.2 What is Doris

Doris是一個MPP的OLAP系統,主要整合了Google Mesa(數據模型),Apache Impala(MPP Query Engine)和Apache ORCFile (存儲格式,編碼和壓縮) 的技術。

apache-doris

Doris的系統架構如下,Doris主要分爲FE和BE兩個組件,FE主要負責查詢的編譯,分發和元數據管理(基於內存,類似HDFS NN);BE主要負責查詢的執行和存儲系統。

apache-doris

2 數據模型

2.1 Kylin的聚合模型

Kylin將表中的列分爲維度列和指標列。在數據導入和查詢時相同維度列中的指標會按照對應的聚合函數(Sum, Count, Min, Max, 精確去重,近似去重,百分位數,TOPN)進行聚合。

在存儲到HBase時,Cuboid+維度 會作爲HBase的Rowkey, 指標會作爲HBase的Value,一般所有指標會在HBase的一個列族,每列對應一個指標,但對於較大的去重指標會單獨拆分到第2個列族。

Kylin-model

2.2 Doris的聚合模型

Doris的聚合模型借鑑自Mesa,但本質上和Kylin的聚合模型一樣,只不過Doris中將維度稱作Key,指標稱作Value。

doris-data-model

Doris中比較獨特的聚合函數是Replace函數,這個聚合函數能夠保證相同Keys的記錄只保留最新的Value,可以藉助這個Replace函數來實現點更新。一般OLAP系統的數據都是隻支持Append的,但是像電商中交易的退款,廣告點擊中的無效點擊處理,都需要去更新之前寫入的單條數據,在Kylin這種沒有Relpace函數的系統中我們必須把包含對應更新記錄的整個Segment數據全部重刷,但是有了Relpace函數,我們只需要再追加1條新的記錄即可。 但是Doris中的Repalce函數有個缺點:無法支持預聚合,就是說只要你的SQL中包含了Repalce函數,即使有其他可以已經預聚合的Sum,Max指標,也必須現場計算。

爲什麼Doirs可以支持點更新呢?

Kylin中的Segment是不可變的,也就是說HFile一旦生成,就不再發生任何變化。但是Doirs中的Segment文件和HBase一樣,是可以進行Compaction的,具體可以參考Google Mesa 論文解讀中的Mesa數據版本化管理

Doris的聚合模型相比Kylin有個缺點:就是一個Column只能有一個預聚合函數,無法設置多個預聚合函數。 不過Doris可以現場計算出其他的聚合函數。 Apache Doris的開發者Review時提到,針對這個問題,Doris還有一種解法:由於Doris支持多表導入的原子更新,所以1個Column需要多個聚合函數時,可以在Doris中建多張表,同一份數據導入時,Doris可以同時原子更新多張Doris表,缺點是多張Doris表的查詢路由需要應用層來完成。

Doris中和Kylin的Cuboid等價的概念是RollUp表,Cuboid和RollUp表都可以認爲是一種Materialized Views或者Index。Doris的RollUp表和Kylin的Cuboid一樣,在查詢時不需要顯示指定,系統內部會根據查詢條件進行路由。 如下圖所示:

Doris Rollup

Doris中RollUp表的路由規則如下:

  1. 選擇包含所有查詢列的RollUp表
  2. 按照過濾和排序的Column篩選最符合的RollUp表
  3. 按照Join的Column篩選最符合的RollUp表
  4. 行數最小的
  5. 列數最小的

2.3 Kylin Cuboid VS Doris RollUp

Kylin cuboid vs Doris rollup

2.4 Doris的明細模型

由於Doris的聚合模型存在下面的缺陷,Doris引入了明細模型。

  • 必須區分維度列和指標列
  • 維度列很多時,Sort的成本很高
  • Count成本很高,需要讀取所有維度列(可以參考Kylin的解決方法進行優化)

Doris的明細模型不會有任何預聚合,不區分維度列和指標列,但是在建表時需要指定Sort Columns,數據導入時會根據Sort Columns進行排序,查詢時根據Sort Column過濾會比較高效

如下圖所示,Sort Columns是Year和City。

Doris-detail-model

這裏需要注意一點,Doris中一張表只能有一種數據模型,即要麼是聚合模型,要麼是明細模型,而且Roll Up表的數據模型必須和Base表一致,也就是說明細模型的Base 表不能有聚合模型的Roll Up表。

3 存儲引擎

Kylin存儲引擎HBase:

Kylin-HBase

如上圖所示,在Kylin中1個Cube可以按照時間拆分爲多個Segment,Segment是Kylin中數據導入和刷新的最小單位。Kylin中1個Segment對應HBase中一張Table。 HBase中的Table會按照Range分區拆分爲多個Region,每個Region會按照大小拆分爲多個HFile。

關於HFile的原理網上講述的文章已經很多了,我這裏簡單介紹下。首先HFile整體上可以分爲元信息,Blcoks,Index3部分,Blcoks和Index都可以分爲Data和Meta兩部分。Block是數據讀取的最小單位,Block有多個Key-Value組成,一個Key-Value代表HBase中的一行記錄,Key-Value由Kylin-Len,Value-Len,Key-Bytes,Value-Bytes 4部分組成。更詳細的信息大家可以參考下圖(下圖來源於互聯網,具體出處不詳):

HBase-HFile

Doris存儲引擎:

屏幕快照 2018-04-18 下午11.19.19.png-125.8kB

如上圖所示,Doris的Table支持二級分區,可以先按照日期列進行一級分區,再按照指定列Hash分桶。具體來說,1個Table可以按照日期列分爲多個Partition, 每個Partition可以包含多個Tablet,Tablet是數據移動、複製等操作的最小物理存儲單元,各個Tablet之間的數據沒有交集,並且在物理上獨立存儲。Partition 可以視爲邏輯上最小的管理單元,數據的導入與刪除,僅能針對一個 Partition進行。1個Table中Tablet的數量= Partition num * Bucket num。Tablet會按照一定大小(256M)拆分爲多個Segment文件,Segment是列存的,但是會按行(1024)拆分爲多個Rowblock。

Doris segment file

下面我們來看下Doris Segment文件的具體格式,Doris文件格式主要參考了Apache ORC。如上圖所示,Doris文件主要由Meta和Data兩部分組成,Meta主要包括文件本身的Header,Segment Meta,Column Meta,和每個Column 數據流的元數據,每部分的具體內容大家看圖即可,比較詳細。 Data部分主要包含每一列的Index和Data,這裏的Index指每一列的Min,Max值和數據流Stream的Position;Data就是每一列具體的數據內容,Data根據不同的數據類型會用不同的Stream來存儲,Present Stream代表每個Value是否是Null,Data Stream代表二進制數據流,Length Stream代表非定長數據類型的長度。 下圖是String使用字典編碼和直接存儲的Stream例子。

Doris String encoding

下面我們來看下Doris的前綴索引:

Doris index

本質上,Doris 的數據存儲是類似 SSTable(Sorted String Table)的數據結構。該結構是一種有序的數據結構,可以按照指定的列有序存儲。在這種數據結構上,以排序列作爲條件進行查找,會非常的高效。而前綴索引,即在排序的基礎上,實現的一種根據給定前綴列,快速查詢數據的索引方式。前綴索引文件的格式如上圖所示,索引的Key是每個Rowblock第一行記錄的Sort Key的前36個字節,Value是Rowblock在Segment文件的偏移量

有了前綴索引後,我們查詢特定Key的過程就是兩次二分查找:

  1. 先加載Index文件,二分查找Index文件獲取包含特定Key的Row blocks的Offest,然後從Sement Files中獲取指定的Rowblock;
  2. 在Rowblocks中二分查找特定的Key

4 數據導入

Kylin數據導入: Kylin data loading

如上圖,Kylin數據導入主要分爲建Hive大寬表(這一步會處理Join);維度列構建字典;逐層構建Cuboid;Cuboid轉爲HFile;Load HFile To HBase; 元數據更新這幾步。

其中Redistribute大寬表這一步的作用是爲了將整個表的數據搞均勻,避免後續的步驟中有數據傾斜,Kylin有配置可以跳過這一步。

其中Extract Distinct Columns這一步的作用是獲取需要構建字典的維度列的Distinct值。假如一個ID維度列有1,2,1,2,2,1,1,2這8行,那麼經過這一步後ID列的值就只有1,2兩行,做這一步是爲了下一步對維度列構建字典時更快速。

其他幾個步驟都比較好理解,我就不再贅述。更詳細的信息可以參考 Apache Kylin Cube 構建原理

Doris數據導入: Doris data loading

Doris 數據導入的兩個核心階段是ETL和LOADING, ETL階段主要完成以下工作:

  • 數據類型和格式的校驗
  • 根據Teblet拆分數據
  • 按照Key列進行排序, 對Value進行聚合

LOADING階段主要完成以下工作:

  • 每個Tablet對應的BE拉取排序好的數據
  • 進行數據的格式轉換,生成索引

LOADING完成後會進行元數據的更新。

5 查詢

Kylin查詢:

Kylin query

如上圖,整個Kylin的查詢過程比較簡單,是個Scatter-Gather的模型。圖中圓形框的內容發生在Kylin QueryServer端,方形框的內容發生在HBase端。Kylin QueryServer端收到SQL後,會先進行SQL的解析,然後生成和優化Plan,再根據Plan生成和編譯代碼,之後會根據Plan生成HBase的Scan請求,如果可能,HBase端除了Scan之外,還會進行過濾和聚合(基於HBase的Coprocessor實現),Kylin會將HBase端返回的結果進行合併,交給Calcite之前生成好的代碼進行計算。

Doris查詢:

Doris-impala-query

Doris的查詢引擎使用的是Impala,是MPP架構。 Doris的FE 主要負責SQL的解析,語法分析,查詢計劃的生成和優化。查詢計劃的生成主要分爲兩步:

  1. 生成單節點查詢計劃 (上圖左下角)
  2. 將單節點的查詢計劃分佈式化,生成PlanFragment(上圖右半部分)

第一步主要包括Plan Tree的生成,謂詞下推, Table Partitions pruning,Column projections,Cost-based優化等;第二步 將單節點的查詢計劃分佈式化,分佈式化的目標是最小化數據移動和最大化本地Scan,分佈式化的方法是增加ExchangeNode,執行計劃樹會以ExchangeNode爲邊界拆分爲PlanFragment,1個PlanFragment封裝了在一臺機器上對同一數據集的部分PlanTree。如上圖所示:各個Fragment的數據流轉和最終的結果發送依賴:DataSink。

當FE生成好查詢計劃樹後,BE對應的各種Plan Node(Scan, Join, Union, Aggregation, Sort等)執行自己負責的操作即可。

6 精確去重

Kylin的精確去重:

Kylin的精確去重是基於全局字典和RoaringBitmap實現的基於預計算的精確去重。具體可以參考 Apache Kylin 精確去重和全局字典權威指南

Doris的精確去重:

Doris的精確去重是現場精確去重,Doris計算精確去重時會拆分爲兩步:

  1. 按照所有的group by 字段和精確去重的字段進行聚合
  2. 按照所有的group by 字段進行聚合
SELECT a, COUNT(DISTINCT b, c), MIN(d), COUNT(*) FROM T GROUP BY a
* - 1st phase grouping exprs: a, b, c
* - 1st phase agg exprs: MIN(d), COUNT(*)
* - 2nd phase grouping exprs: a
* - 2nd phase agg exprs: COUNT(*), MIN(<MIN(d) from 1st phase>), SUM(<COUNT(*) from 1st phase>)

下面是個簡單的等價轉換的例子:

select count(distinct lo_ordtotalprice) from ssb_sf20.v2_lineorder;

select count(*) from (select count(*) from ssb_sf20.v2_lineorder group by lo_ordtotalprice) a;

Doris現場精確去重計算性能和去重列的基數去重指標個數過濾後的數據大小負相關

7 元數據

Kylin的元數據 :

Kylin的元數據是利用HBase存儲的,可以很好地橫向擴展。Kylin每個具體的元數據都是一個Json文件,HBase的Rowkey是文件名,Value是Json文件的內容。Kylin的元數據表設置了IN_MEMORY => 'true' 屬性, 元數據表會常駐HBase RegionServer的內存,所以元數據的查詢性能很好,一般在幾ms到幾十ms。

Kylin元數據利用HBase存儲的一個問題是,在Kylin可插拔架構下,即使我們實現了另一種存儲引擎,我們也必須部署HBase來存儲元數據,所以Kylin要真正做到存儲引擎的可插拔,就必須實現一個獨立的元數據存儲。

Doris的元數據

Doris的元數據是基於內存的,這樣做的好處是性能很好且不需要額外的系統依賴。 缺點是單機的內存是有限的,擴展能力受限,但是根據Doris開發者的反饋,由於Doris本身的元數據不多,所以元數據本身佔用的內存不是很多,目前用大內存的物理機,應該可以支撐數百臺機器的OLAP集羣。 此外,OLAP系統和HDFS這種分佈式存儲系統不一樣,我們部署多個集羣的運維成本和1個集羣區別不大。

關於Doris元數據的具體原理大家可以參考Doris官方文檔Doris 元數據設計文檔

8 高性能

Why Kylin Query Fast:

Kylin query

Kylin查詢快的核心原因就是預計算,如圖(圖片出處 Apache kylin 2.0: from classic olap to real-time data warehouse),Kylin現場查詢時不需要Join,也幾乎不需要聚合,主要就是Scan + Filter

Why Doris Query Fast:

  1. In-Memory Metadata。 Doris的元數據就在內存中,元數據訪問速度很快。
  2. 聚合模型可以在數據導入時進行預聚合。
  3. 和Kylin一樣,也支持預計算的RollUp Table。
  4. MPP的查詢引擎。
  5. 向量化執行。相比Kylin中Calcite的代碼生成,向量化執行在處理高併發的低延遲查詢時性能更好,Kylin的代碼生成本身可能會花費幾十ms甚至幾百ms
  6. 列式存儲 + 前綴索引。

9 高可用

Kylin高可用:

Kylin JobServer的高可用: Kylin的JobServer是無狀態的,一臺JobServer掛掉後,其他JobServer會很快接管正在Running的Job。JobServer的高可用是基於Zookeeper實現的,具體可以參考Apache Kylin Job 生成和調度詳解

Kylin QueryServer的高可用:Kylin的QueryServer也是無狀態的,其高可用一般通過Nginx這類的負載均衡組件來實現。

Kylin Hadoop依賴的高可用: 要單純保證Kylin自身組件的高可用並不困難,但是要保證Kylin整體數據導入和查詢的高可用是十分困難的,因爲必須同時保證HBase,Hive,Hive Metastore,Spark,Mapreduce,HDFS,Yarn,Zookeeper,Kerberos這些服務的高可用。

Doris高可用:

Doris FE的高可用: Doris FE的高可用主要基於BerkeleyDB java version實現,BDB-JE實現了類Paxos一致性協議算法

Doris BE的高可用: Doris會保證每個Tablet的多個副本分配到不同的BE上,所以一個BE down掉,不會影響查詢的可用性。

10 可維護性

10.1 部署

Kylin部署:如果完全從零開始,你就需要部署1個Hadoop集羣和HBase集羣。 即使公司已經有了比較完整的Hadoop生態,在部署Kylin前,你也必須先部署Hadoop客戶端,HBase客戶端,Hive客戶端,Spark客戶端。

Doris部署: 直接部署FE和BE組件即可。

10.2 運維

Kylin運維: 運維Kylin對Admin有較高的要求,首先必須瞭解HBase,Hive,MapReduce,Spark,HDFS,Yarn的原理;其次對MapReduce Job和Spark Job的問題排查和調優經驗要豐富;然後必須掌握對Cube複雜調優的方法;最後出現問題時排查的鏈路較長,複雜度較高。

Doris運維: Doris只需要理解和掌握系統本身即可。

10.3 客服

Kylin 客服: 需要向用戶講清Hadoop相關的一堆概念;需要教會用戶Kylin Web的使用;需要教會用戶如何進行Cube優化(沒有統一,簡潔的優化原則);需要教會用戶怎麼查看MR和Spark日誌;需要教會用戶怎麼查詢;

Doris 客服: 需要教會用戶聚合模型,明細模型,前綴索引,RollUp表這些概念。

11 易用性

11.1 查詢接入

Kylin查詢接入:Kylin支持Htpp,JDBC,ODBC 3種查詢方式。

Doris查詢接入: Doris支持Mysql協議,現有的大量Mysql工具都可以直接使用,用戶的學習和遷移成本較低。

11.2 學習成本

Kylin學習成本:用戶要用好Kylin,需要理解以下概念:

  • Cuboid
  • 聚集組
  • 強制維度
  • 聯合維度
  • 層次維度
  • 衍生維度
  • Extend Column
  • HBase RowKey 順序

此外,前面提到過,用戶還需要學會怎麼看Mapreduce Job和Spark Job日誌。

Doris學習成本:用戶需要理解聚合模型,明細模型,前綴索引,RollUp表這些概念。

11.3 Schema Change

Schema在線變更是一個十分重要的feature,因爲在實際業務中,Schema的變更會十分頻繁。

Kylin Schema Change: Kylin中用戶對Cube Schema的任何改變,都需要在Staging環境重刷所有數據,然後切到Prod環境。整個過程週期很長,資源浪費比較嚴重

Doris Schema Change:Doris支持Online Schema Change。

所謂的Schema在線變更就是指Scheme的變更不會影響數據的正常導入和查詢,Doris中的Schema在線變更有3種:

  • direct schema change:就是重刷全量數據,成本最高,和kylin的做法類似。當修改列的類型,稀疏索引中加一列時需要按照這種方法進行。
  • sorted schema change: 改變了列的排序方式,需對數據進行重新排序。例如刪除排序列中的一列, 字段重排序。
  • linked schema change: 無需轉換數據,直接完成。對於歷史數據不會重刷,新攝入的數據都按照新的Schema處理,對於舊數據,新加列的值直接用對應數據類型的默認值填充。例如加列操作。Druid也支持這種做法。

12 功能

Apache Kylin VS Apache Doris

注: 關於Kylin的明細查詢,Kylin本身只有聚合模型,但是也可以通過將所有列作爲維度列,只構建Base Cuboid來實現明細查詢, 缺點是效率比較低下。

注: 雖然Doirs理論上可以同時支持高併發,低延遲的OLAP查詢和高吞吐的Adhoc查詢,但顯然這兩類查詢會相互影響。所以Baidu在實際應用中也是用兩個集羣分別滿足OLAP查詢和Adhoc查詢需求。

13 社區和生態

Doris社區剛剛起步,目前核心用戶只有Baidu;Kylin的社區和生態已經比較成熟,Kylin是第一個完全由中國開發者貢獻的Apache頂級開源項目,目前已經在多家大型公司的生產環境中使用。

14 總結

本文從多方面對比了Apache Kylin和Apache Doris,有理解錯誤的地方歡迎指正。本文更多的是對兩個系統架構和原理的客觀描述,主觀判斷較少。最近在調研了Doirs,ClickHouse,TiDB之後,也一直在思考OLAP系統的發展趨勢是怎樣的下一代更優秀的OLAP系統架構應該是怎樣的一個系統是否可以同時很好的支持OLTP和OLAP,這些問題想清楚後我會再寫篇文章描述下,當然,大家有好的想法,也歡迎直接Comment。

15 參考資料

1 Doris文檔和源碼

2 Kylin源碼

3 Apache kylin 2.0: from classic olap to real-time data warehouse 在Kylin高性能部分引用了第4頁PPT的截圖

4 百度MPP數據倉庫Palo開源架構解讀與應用 在Doris查詢部分引用了第31頁PPT的截圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章