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