連續兩篇文章都聊了不同的存儲格式,這篇我們繼續深入來看看在存儲格式的演變之上有什么新的"黑科技"。華為公司在2016年開源了類parquet的列存格式:CarbonData,並且貢獻給了Apache社區。CarbonData僅僅用了不到一年的時間就成功畢業,成為了Apache社區的頂級項目,CarbonData是首個由華人公司主導的Apache頂級項目,(來源自eBay的Kylin算是首個由華人主導的頂級開源項目)筆者這里還是要向華為的小伙伴們致敬,能夠完成這樣一個從0到1的突破。
本篇筆者嘗試從技術細節來梳理CarbonData與其“前輩”到底有何不同之處,我們在實際應用與設計存儲格式時有什么可以借鑒汲取之處。
1.CarbonData
首先我們來看看CarbonData本身的定位,如下圖所示:

- 1、支持海量數據掃描並取其中幾列;
- 2、支持根據主鍵進行查找,並在秒級響應;
- 3、支持在海量數據進行類似於OLAP的交互式查詢,並且查詢中涉及到許多過濾條件,這種類型的workload應該在幾秒鍾內響應;
- 4、支持快速地抽取單獨的記錄,並且從該記錄中獲取到所有列信息;
- 5、支持HDFS,無縫對接Hadoop生態圈,天生帶有分布式基因。
對於OLAP查詢來說,存在多種不同類型的查詢,存儲結構的不同會影響到不同查詢的數據表現。所以CarbonData的定位是作為一種通用的查詢存儲數據,通過Spark SQL來解決海量查詢的問題,並且能夠與Hadoop生態圈進行無縫對接。CarbonData最初的應用是與Spark SQL和Spark DataFrame深度結合,后續由攜程團隊將CarbonData引入了Presto,滴滴團隊將CarbonData引入Hive。
其實無論是多維的OLAP查詢,還是完整的掃描查詢,還是部分范圍查詢。CarbonData的前輩ORCFile與Parquet都可以同樣完成任務,那么作為新人,CarbonData有什么過人之處呢?
快,更快
下圖是華為提供進行實測的數據,在絕大多數的測試場景之中CarbonData的性能都略優於Parquet。

當然快速的查詢是需要付出代價的,查詢的快速所犧牲的是壓縮率的減小與入庫時間的延長。

那我們接下來就是要詳盡討論CarbonData的性能表現與底層設計之間的邏輯關系。
文件結構
下圖展示了CarbonData的數據存儲格式:

-
File Header
文件頭的格式比較簡單,保存了存儲格式的版本和模式信息。(這部分通常是穩定不可變的內容) -
Blocklet
單Blocklet最大的容量閥值為64M,也就是說單個HDFS的Block可以容納多個Blocklet(視Block的大小而定)。這塊內容與ORCFile與Parquet的設計一脈相承,都是利用Pax的存儲模型來優化數據查詢時的性能表現。 -
File Footer
在文件尾部保存了存儲數據的索引和摘要,索引是CarbonData最為核心的關鍵實現,正是由於索引的存在,大大提高了CarbonData在不同查詢場景之下的性能表現。
二級索引
CarbonData通過支持了二級索引,大大的提高了CarbonData數據查詢的性能表現。

由上圖所示CarbonData在HDFS Block級別與內部的Blocklet級別都分別建立起索引,這樣可以大大減少非必要的任務啟動與非必要的磁盤IO操作。眾所周知,引入索引的的確確能夠加快數據的查詢速率,但是天下沒有免費的午餐。我想CarbonData壓縮率縮減與數據導入時間的延長的原因,想必讀者心中也有了答案。

我們可以看到在CarbonData的文件尾部,通過B+樹的方式來實現索引。由於HDFS追加寫的特性,所以我想讀者應該也能明白為何這些索引數據與統計數據需要存放在CarbonData的末尾。

上圖完整的展現了一次過濾查詢的流程,這個過程在二級索引的作用之下,規避了大量非必要的查詢交互,由此帶來的性能優化是十分明顯的。
相對於ORCFile與Parquet相對簡要的摘要索引,CarbonData在索引層面頗費心思。通過這樣的方式來超越前輩,當然這樣的選擇設計同樣也要付出額外的代價。
全局字典編碼
這是CarbonData之中頗具爭議的功能,在CarbonData之前的版本是默認添加的內容,目前在1.3版本之中是作為可選項加入其中的。(筆者在華為高斯部門工作的師兄也曾經和筆者吐槽過在生產環境之中,全局字典編碼的似乎還存在一些'坑')所以看起來能夠運用好字典編碼的確是個值得探討的問題,筆者在此也簡單聊一聊:

如上圖所示,全局字典編碼的方式很簡單,就是通過數字和字典來替換表格之中重復出現的數據。 這樣的好處很明顯:
-
大大減少了表格數據所需要存儲的數據量
-
某些需要進行group by的字段進行全局字典編碼,可以大量減少計算時的shuffle的數據量。以達到性能提升的目的。
但是在將數據導入CarbonData的過程之中,對與重復率較低的列,一旦建立起全局字典,顯然會大大拖慢數據的導入速度,並且影響數據的壓縮程度。而如果對於數據重復率較高的數據,例如性別,年齡等高重復數據,通過建立全局字典能夠大大提升CarbonData的壓縮程度,並且對數據導入的速率影響不大。
筆者建議:對於字典編碼的使用,還是要根據具體也業務場景進行分析壓測,給出較為合適的使用方式,盲目使用字典編碼反而會對性能帶來負優化。
2.小結
到此為止,筆者也大致聊完了對CarbonData存儲結構的理解以及筆者在簡單實踐之中所引發的思考。 作為華人圈子之中首個由華人公司主導的Apache的頂級項目,筆者也會繼續對CarbonData進行關注與學習,也希望將來華人程序員能夠在開源圈之中繼續擴大影響力。
