面試官:相比于MySQL,你覺得ClickHouse牛在哪兒?
有時候真的挺替MySQL鳴不平的,被最廣泛地應用在各個系統中,卻當著Redis、ES、Oracle的背景板,挨著最狠地罵。
嗯,今天又拿它跟OLAP數據庫ClickHouse進行比較了。
一般來講,90%多的Java工程師是接觸不到ClickHouse的,而用到它的最大原因,無非是拿MySQL硬抗海量數據的統計分析類的場景實在太吃力了,臨戰換將成ClickHouse之后,頓覺世界時如此美好。
接下來我們就來說說,ClickHouse到底有多牛逼,以及牛逼在哪兒。
對比數據
在一系列官方公布的基準測試對比中,ClickHouse都遙遙領先對手,這其中不乏一些我們耳熟能詳的名字。
所有用于對比的數據庫都使用了相同配置的服務器,在單個節點的情況下,對一張擁有133個字段的數據表分別在1000萬、1億和10億三種數據體量下執行基準測試,基準測試的范圍涵蓋43項SQL查詢。
在1億數據集體量的情況下,ClickHouse的平均響應速度是Vertica的2.63倍、InfiniDB的17倍、MonetDB的27倍、Hive的126倍、MySQL的429倍以及Greenplum的10倍。
下圖也是ClickHouse官網上公布的測試數據:
圖片
接下來我們具體分析一下,ClickHouse到底具備那些特性,以至于它比MySQL的性能高出如此之多。
MPP架構
ClickHouse采用典型的MPP(Massively Parallel Processing)架構,直譯為大規模并行處理架構,可將任務并行的分散到多個節點上,每個節點各自獨立完成自己的計算任務,然后將各節點的處理結果進行二次加工匯總,形成最終的結果進行返回。
如下圖所示:
圖片
MPP架構中的各個計算節點相互獨立,具備高性能和易擴展的優點,可在海量數據下實現高吞吐量和低延遲的數據處理能力。
而MySQL InnoDB本身是不具備分布式并行計算能力的,一般情況下都是用分庫分表中間件進行實現的,也有少部分是直接在工程代碼中進行實現。
當然,從完善度和平滑性來講,肯定是不如ClickHouse的一體化解決方案的。
列式存儲
假設這樣的一個業務場景,一張有2000多萬條記錄的people表,我們需要計算出表中所有人的平均年齡。
如下圖所示:
圖片
對MySQL InnoDB存儲引擎比較熟悉的同學都知道,其存儲結構是按照表空間(tablespace)——>段(segment)——>區(extent)——>page(頁)的方式進行組織的,而page是MySQL InnoDB的最小IO單元,默認為16k。
而page中存儲的才是MySQL InnoDB的行記錄(row),每行記錄中有若干個列字段值。
如下圖所示:
圖片
那么,對于MySQL InnoDB存儲引擎的行式數據庫來說,如果需要計算people全表的平均年齡,那就需要以page為IO單元全表掃描出所有數據,再把里面的age數據挑出來進行計算,才能得出最終的結果。
而Clickhouse的存儲形式則完全不一樣了,它是以數據列的方式進行組織存儲的,每個列字段都擁有獨立的.bin數據文件,并以列字段的名稱命名。
如下圖所示:
圖片
如果需要計算people全表的平均年齡的話,ClickHouse只需要讀取age.bin文件中的數據進行計算即可。
這樣一來,假設people表中有20個字段,對于計算出表中所有人的平均年齡的需求,ClickHouse所需要讀取的數據量只有MySQL InnoDB的大約1/20,那自然想性能不高都難。
我們來看一下,ClickHouse官網對于行式存儲和列式存儲的對比配圖,還是比較形象的。
行式存儲:
圖片
列式存儲:
圖片
數據壓縮
由于壓縮后的數據不僅可以節省存儲空間,還可以減少磁盤IO和網絡傳輸的數據量,這是高性能數據庫必不可少的特性。
并且,ClickHouse列式數據庫比MySQL InnoDB存儲引擎的行式數據庫,對數據壓縮更加友好。
原因在于,在ClickHouse最常用的MergeTree表引擎中,數據表中的同一列數據是保存在一個文件里的,其擁有相同的數據類型和業務語義,重復項的可能性自然就很高。比如:訂單金額、個人年齡、工作收入等。
而重復項越高,文件壓縮率和數據體量就越小,也就越能減少磁盤IO和網絡傳輸的壓力。
ClickHouse默認使用LZ4算法進行數據壓縮的,壓縮比可以達到8:1。
再來說說MySQL InnoDB,可以通過如下SQL語句進行數據壓縮,但壓縮效果一般,僅可以節省30%到50%的磁盤空間。
ALTER TABLE sbtest1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8
并且通過性能測試得知,數據壓縮對數據庫服務器的負載和CPU使用率影響較大,在性能上也并無提升。
這樣看來,MySQL InnoDB的數據壓縮僅能節省有限的磁盤空間,效果比較雞肋。
向量化執行引擎
像Flink、Spark、Storm這些分布式計算框架,會將一個大任務拆解成若干個可以并行執行的小任務,以此來提升整體的任務處理吞吐量。
而ClickHouse為了最大限度地將CPU的性能壓榨到極致,實現了向量化執行引擎,其核心思想是充分利用現代處理器的并行處理能力,來提升代碼執行效率。
向量化執行引擎可以對一組數據執行相同的一個指令,在訪問速度最快的寄存器層面實現單指令、多數據(SIMD )操作,以實現空間上的并行。
各硬件訪問速度對比,如下圖:
圖片
SIMD操作方式,如下圖:
圖片
索引設計
ClickHouse與MySQL InnoDB的索引設計方式截然不同,它的一級索引是采用稀疏索引的方式進行實現的。
對比如下:
圖片
在上圖左側的稠密索引中,索引標記和數據記錄是一一對應的,而圖右側的稀疏索引則對應的是包含N行記錄的一個數據塊。
稀疏索引的優點是,僅需要少量索引標記就可以記錄海量數據的區間位置信息。如果索引粒度是默認的8192,那一億條數據僅需要對應12208個索引標記,這樣就可以將索引標記放到內存中,可以起到提升查詢性能的效果。
在二級索引方面,MySQL InnoDB只支持B+ Tree和Hash兩種類型,而ClickHouse則支持minmax、set、bloom_filter、ngrambf_v1、tokenbf_v1和inverted等索引類型。
minmax索引,用來記錄N(N = granularity)個數據塊內的最大值和最小值,在對某列數據進行范圍查詢的時候,可以過濾掉不滿足條件的數據區間,其適用于數據區分度比較高的場景。
如下圖所示:
圖片
set索引,用來記錄每個數據塊中的不重復值,舉個例子,如果該數據塊所對應的列中有8000個1,190個2,1個3和1個4,那set中所記錄的值就是(1,2,3,4)。
set索引的適用場景為,在區分度低的列上查找列值很少的數據行。比如:在訂單表中查詢狀態為“退款”的訂單。
bloom_filter索引,顧名思義,是用來構建布隆過濾器的,默認有0.025(可調整)的誤差率,會將原本不存在的值誤認為已存在。但用在二級索引上是不影響正確性的,僅僅是多查詢了一些數據塊而已。