天天看點

Apache Kylin VS Apache Doris全方位對比

  • 1 系統架構
    • 1.1 What is Kylin
    • 1.2 What is Doris
  • 2 資料模型
    • 2.1 Kylin的聚合模型
    • 2.2 Doris的聚合模型
    • 2.3 Kylin Cuboid VS Doris RollUp
    • 2.4 Doris的明細模型
  • 3 存儲引擎
  • 4 資料導入
  • 5 查詢
  • 6 精确去重
  • 7 中繼資料
  • 8 高性能
  • 9 高可用
  • 10 可維護性
    • 10.1 部署
    • 10.2 運維
    • 10.3 客服
  • 11 易用性
    • 11.1 查詢接入
    • 11.2 學習成本
    • 11.3 Schema Change
  • 12 功能
  • 13 社群和生态
  • 14 總結
  • 15 參考資料

本文作者:康凱森,來源于:https://blog.bcmeng.com,文章寫的非常詳細,從各個方面對Kylin和Doris進行了對比。

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。

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

Apache Kylin VS Apache Doris全方位對比

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

Apache Kylin VS Apache Doris全方位對比

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

Apache Kylin VS Apache Doris全方位對比

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

Apache Kylin VS Apache Doris全方位對比

Doris的系統架構如下,Doris主要分為FE和BE兩個元件,FE主要負責查詢的編譯,分發和中繼資料管理(基于記憶體,類似HDFS NN);BE主要負責查詢的執行和存儲系統。

Apache Kylin VS Apache Doris全方位對比

Kylin将表中的列分為次元列和名額列。在資料導入和查詢時相同次元列中的名額會按照對應的聚合函數(Sum, Count, Min, Max, 精确去重,近似去重,百分位數,TOPN)進行聚合。

在存儲到HBase時,Cuboid+次元 會作為HBase的Rowkey, 名額會作為HBase的Value,一般所有名額會在HBase的一個列族,每列對應一個名額,但對于較大的去重名額會單獨拆分到第2個列族。

Apache Kylin VS Apache Doris全方位對比

Doris的聚合模型借鑒自Mesa,但本質上和Kylin的聚合模型一樣,隻不過Doris中将次元稱作Key,名額稱作Value。

Apache Kylin VS Apache Doris全方位對比

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一樣,在查詢時不需要顯示指定,系統内部會根據查詢條件進行路由。 如下圖所示:

Apache Kylin VS Apache Doris全方位對比

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

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

Apache Kylin VS Apache Doris全方位對比

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

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

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

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

Apache Kylin VS Apache Doris全方位對比

這裡需要注意一點,Doris中一張表隻能有一種資料模型,即要麼是聚合模型,要麼是明細模型,而且Roll Up表的資料模型必須和Base表一緻,也就是說明細模型的Base 表不能有聚合模型的Roll Up表。

Kylin存儲引擎HBase:

Apache Kylin VS Apache Doris全方位對比

如上圖所示,在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部分組成。更詳細的資訊大家可以參考下圖(下圖來源于網際網路,具體出處不詳):

Apache Kylin VS Apache Doris全方位對比

Doris存儲引擎:

Apache Kylin VS Apache Doris全方位對比

如上圖所示,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。

Apache Kylin VS Apache Doris全方位對比

下面我們來看下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例子。

Apache Kylin VS Apache Doris全方位對比

下面我們來看下Doris的字首索引:

Apache Kylin VS Apache Doris全方位對比

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

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

  • 先加載Index檔案,二分查找Index檔案擷取包含特定Key的Row blocks的Offest,然後從Sement Files中擷取指定的Rowblock;
  • 在Rowblocks中二分查找特定的Key

Kylin資料導入:

Apache Kylin VS Apache Doris全方位對比

如上圖,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資料導入:

Apache Kylin VS Apache Doris全方位對比

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

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

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

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

LOADING完成後會進行中繼資料的更新。

Kylin查詢:

Apache Kylin VS Apache Doris全方位對比

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

Doris查詢:

Apache Kylin VS Apache Doris全方位對比

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

  • 生成單節點查詢計劃 (上圖左下角)
  • 将單節點的查詢計劃分布式化,生成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等)執行自己負責的操作即可。

Kylin的精确去重:

Kylin的精确去重是基于全局字典和RoaringBitmap實作的基于預計算的精确去重。

Doris的精确去重:

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

  • 按照所有的group by 字段和精确去重的字段進行聚合
  • 按照所有的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現場精确去重計算性能和去重列的基數、去重名額個數、過濾後的資料大小成負相關;

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 中繼資料設計文檔

Why Kylin Query Fast:

Apache Kylin VS Apache Doris全方位對比

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

Why Doris Query Fast:

  • In-Memory Metadata。

    Doris的中繼資料就在記憶體中,中繼資料通路速度很快。

  • 聚合模型可以在資料導入時進行預聚合。
  • 和Kylin一樣,也支援預計算的RollUp Table。
  • MPP的查詢引擎。
  • 向量化執行。

    相比Kylin中Calcite的代碼生成,向量化執行在處理高并發的低延遲查詢時性能更好,Kylin的代碼生成本身可能會花費幾十ms甚至幾百ms。

  • 列式存儲 + 字首索引。

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掉,不會影響查詢的可用性。

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

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

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

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

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

Doris 客服: 需要教會使用者聚合模型,明細模型,字首索引,RollUp表這些概念。

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

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

Kylin學習成本:使用者要用好Kylin,需要了解以下概念:

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

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

Doris學習成本:使用者需要了解聚合模型,明細模型,字首索引,RollUp表這些概念。

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也支援這種做法。

Apache Kylin VS Apache Doris全方位對比

注:關于Kylin的明細查詢,Kylin本身隻有聚合模型,但是也可以通過将所有列作為次元列,隻建構Base Cuboid來實作明細查詢, 缺點是效率比較低下。

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

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

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

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的截圖

繼續閱讀