自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

比MySQL快801倍!ClickHouse這么牛嗎?是的,簡(jiǎn)直開掛!

運(yùn)維 數(shù)據(jù)庫(kù)運(yùn)維
初識(shí)ClickHouse的時(shí)候,我曾產(chǎn)生這樣的感覺:它仿佛違背了物理定律,沒有任何缺點(diǎn),是一個(gè)不真實(shí)的存在。一款高性能、高可用OLAP數(shù)據(jù)庫(kù)的一切訴求,ClickHouse似乎都能滿足,這股神秘的氣息引起了我極大的好奇。

[[410122]]

隨著業(yè)務(wù)的迅猛增長(zhǎng),Yandex.Metrica目前已經(jīng)成為世界第三大Web流量分析平臺(tái),每天處理超過200億個(gè)跟蹤事件。能夠擁有如此驚人的體量,在它背后提供支撐的ClickHouse功不可沒。ClickHouse已經(jīng)為Yandex.Metrica存儲(chǔ)了超過20萬(wàn)億行的數(shù)據(jù),90%的自定義查詢能夠在1秒內(nèi)返回,其集群規(guī)模也超過了400臺(tái)服務(wù)器。雖然ClickHouse起初只是為了Yandex.Metrica而研發(fā)的,但由于它出眾的性能,目前也被廣泛應(yīng)用于Yandex內(nèi)部其他數(shù)十個(gè)產(chǎn)品上。

初識(shí)ClickHouse的時(shí)候,我曾產(chǎn)生這樣的感覺:它仿佛違背了物理定律,沒有任何缺點(diǎn),是一個(gè)不真實(shí)的存在。一款高性能、高可用OLAP數(shù)據(jù)庫(kù)的一切訴求,ClickHouse似乎都能滿足,這股神秘的氣息引起了我極大的好奇。

剛從Hadoop生態(tài)轉(zhuǎn)向ClickHouse的時(shí)候,我曾有諸多的不適應(yīng),因?yàn)樗臀覀兺J褂玫募夹g(shù)"性格"迥然不同。如果把數(shù)據(jù)庫(kù)比作汽車,那么ClickHouse儼然就是一輛手動(dòng)擋的賽車。它在很多方面不像其他系統(tǒng)那樣高度自動(dòng)化。ClickHouse的一些概念也與我們通常的理解有所不同,特別是在分片和副本方面,有些時(shí)候數(shù)據(jù)的分片甚至需要手動(dòng)完成。在進(jìn)一步深入使用ClickHouse之后,我漸漸地理解了這些設(shè)計(jì)的目的。

某些看似不夠自動(dòng)化的設(shè)計(jì),反過來(lái)卻在使用中帶來(lái)了極大的靈活性。與Hadoop生態(tài)的其他數(shù)據(jù)庫(kù)相比,ClickHouse更像一款"傳統(tǒng)"MPP架構(gòu)的數(shù)據(jù)庫(kù),它沒有采用Hadoop生態(tài)中常用的主從架構(gòu),而是使用了多主對(duì)等網(wǎng)絡(luò)結(jié)構(gòu),同時(shí)它也是基于關(guān)系模型的ROLAP方案。

本文就讓我們抽絲剝繭,看看ClickHouse都有哪些核心特性。

ClickHouse的核心特性

ClickHouse是一款MPP架構(gòu)的列式存儲(chǔ)數(shù)據(jù)庫(kù),但MPP和列式存儲(chǔ)并不是什么"稀罕"的設(shè)計(jì)。擁有類似架構(gòu)的其他數(shù)據(jù)庫(kù)產(chǎn)品也有很多,但是為什么偏偏只有ClickHouse的性能如此出眾呢?通過上一章的介紹,我們知道了ClickHouse發(fā)展至今的演進(jìn)過程。它一共經(jīng)歷了四個(gè)階段,每一次階段演進(jìn),相比之前都進(jìn)一步取其精華去其糟粕??梢哉fClickHouse汲取了各家技術(shù)的精髓,將每一個(gè)細(xì)節(jié)都做到了極致。接下來(lái)將介紹ClickHouse的一些核心特性,正是這些特性形成的合力使得ClickHouse如此優(yōu)秀。

1.完備的DBMS功能

ClickHouse擁有完備的管理功能,所以它稱得上是一個(gè)DBMS ( Database Management System,數(shù)據(jù)庫(kù)管理系統(tǒng) ),而不僅是一個(gè)數(shù)據(jù)庫(kù)。作為一個(gè)DBMS,它具備了一些基本功能,如下所示。

  • DDL ( 數(shù)據(jù)定義語(yǔ)言 ):可以動(dòng)態(tài)地創(chuàng)建、修改或刪除數(shù)據(jù)庫(kù)、表和視圖,而無(wú)須重啟服務(wù)。

  • DML ( 數(shù)據(jù)操作語(yǔ)言 ):可以動(dòng)態(tài)查詢、插入、修改或刪除數(shù)據(jù)。

  • 權(quán)限控制:可以按照用戶粒度設(shè)置數(shù)據(jù)庫(kù)或者表的操作權(quán)限,保障數(shù)據(jù)的安全性。

  • 數(shù)據(jù)備份與恢復(fù):提供了數(shù)據(jù)備份導(dǎo)出與導(dǎo)入恢復(fù)機(jī)制,滿足生產(chǎn)環(huán)境的要求。

  • 分布式管理:提供集群模式,能夠自動(dòng)管理多個(gè)數(shù)據(jù)庫(kù)節(jié)點(diǎn)。

這里只列舉了一些最具代表性的功能,但已然足以表明為什么Click House稱得上是DBMS了。

2.列式存儲(chǔ)與數(shù)據(jù)壓縮

列式存儲(chǔ)和數(shù)據(jù)壓縮,對(duì)于一款高性能數(shù)據(jù)庫(kù)來(lái)說是必不可少的特性。一個(gè)非常流行的觀點(diǎn)認(rèn)為,如果你想讓查詢變得更快,最簡(jiǎn)單且有效的方法是減少數(shù)據(jù)掃描范圍和數(shù)據(jù)傳輸時(shí)的大小,而列式存儲(chǔ)和數(shù)據(jù)壓縮就可以幫助我們實(shí)現(xiàn)上述兩點(diǎn)。列式存儲(chǔ)和數(shù)據(jù)壓縮通常是伴生的,因?yàn)橐话銇?lái)說列式存儲(chǔ)是數(shù)據(jù)壓縮的前提。

按列存儲(chǔ)與按行存儲(chǔ)相比,前者可以有效減少查詢時(shí)所需掃描的數(shù)據(jù)量,這一點(diǎn)可以用一個(gè)示例簡(jiǎn)單說明。假設(shè)一張數(shù)據(jù)表A擁有50個(gè)字段A1~A50,以及100行數(shù)據(jù)?,F(xiàn)在需要查詢前5個(gè)字段并進(jìn)行數(shù)據(jù)分析,則可以用如下SQL實(shí)現(xiàn):

  1. SELECT A1,A2,A3,A4,A5 FROM A 

如果數(shù)據(jù)按行存儲(chǔ),數(shù)據(jù)庫(kù)首先會(huì)逐行掃描,并獲取每行數(shù)據(jù)的所有50個(gè)字段,再?gòu)拿恳恍袛?shù)據(jù)中返回A1~A5這5個(gè)字段。不難發(fā)現(xiàn),盡管只需要前面的5個(gè)字段,但由于數(shù)據(jù)是按行進(jìn)行組織的,實(shí)際上還是掃描了所有的字段。如果數(shù)據(jù)按列存儲(chǔ),就不會(huì)發(fā)生這樣的問題。由于數(shù)據(jù)按列組織,數(shù)據(jù)庫(kù)可以直接獲取A1~A5這5列的數(shù)據(jù),從而避免了多余的數(shù)據(jù)掃描。

按列存儲(chǔ)相比按行存儲(chǔ)的另一個(gè)優(yōu)勢(shì)是對(duì)數(shù)據(jù)壓縮的友好性。同樣可以用一個(gè)示例簡(jiǎn)單說明壓縮的本質(zhì)是什么。假設(shè)有兩個(gè)字符串a(chǎn)bcdefghi和bcdefghi,現(xiàn)在對(duì)它們進(jìn)行壓縮,如下所示:

  1. 壓縮前:abcdefghi_bcdefghi 
  2. 壓縮后:abcdefghi_(9,8

可以看到,壓縮的本質(zhì)是按照一定步長(zhǎng)對(duì)數(shù)據(jù)進(jìn)行匹配掃描,當(dāng)發(fā)現(xiàn)重復(fù)部分的時(shí)候就進(jìn)行編碼轉(zhuǎn)換。例如上述示例中的 (9,8),表示如果從下劃線開始向前移動(dòng)9個(gè)字節(jié),會(huì)匹配到8個(gè)字節(jié)長(zhǎng)度的重復(fù)項(xiàng),即這里的bcdefghi。

真實(shí)的壓縮算法自然比這個(gè)示例更為復(fù)雜,但壓縮的實(shí)質(zhì)就是如此。數(shù)據(jù)中的重復(fù)項(xiàng)越多,則壓縮率越高;壓縮率越高,則數(shù)據(jù)體量越?。欢鴶?shù)據(jù)體量越小,則數(shù)據(jù)在網(wǎng)絡(luò)中的傳輸越快,對(duì)網(wǎng)絡(luò)帶寬和磁盤IO的壓力也就越小。既然如此,那怎樣的數(shù)據(jù)最可能具備重復(fù)的特性呢?答案是屬于同一個(gè)列字段的數(shù)據(jù),因?yàn)樗鼈儞碛邢嗤臄?shù)據(jù)類型和現(xiàn)實(shí)語(yǔ)義,重復(fù)項(xiàng)的可能性自然就更高。

ClickHouse就是一款使用列式存儲(chǔ)的數(shù)據(jù)庫(kù),數(shù)據(jù)按列進(jìn)行組織,屬于同一列的數(shù)據(jù)會(huì)被保存在一起,列與列之間也會(huì)由不同的文件分別保存 ( 這里主要指MergeTree表引擎 )。數(shù)據(jù)默認(rèn)使用LZ4算法壓縮,在Yandex.Metrica的生產(chǎn)環(huán)境中,數(shù)據(jù)總體的壓縮比可以達(dá)到8:1 ( 未壓縮前17PB,壓縮后2PB )。列式存儲(chǔ)除了降低IO和存儲(chǔ)的壓力之外,還為向量化執(zhí)行做好了鋪墊。

3.向量化執(zhí)行引擎

坊間有句玩笑,即"能用錢解決的問題,千萬(wàn)別花時(shí)間"。而業(yè)界也有種調(diào)侃如出一轍,即"能升級(jí)硬件解決的問題,千萬(wàn)別優(yōu)化程序"。有時(shí)候,你千辛萬(wàn)苦優(yōu)化程序邏輯帶來(lái)的性能提升,還不如直接升級(jí)硬件來(lái)得簡(jiǎn)單直接。這雖然只是一句玩笑不能當(dāng)真,但硬件層面的優(yōu)化確實(shí)是最直接、最高效的提升途徑之一。向量化執(zhí)行就是這種方式的典型代表,這項(xiàng)寄存器硬件層面的特性,為上層應(yīng)用程序的性能帶來(lái)了指數(shù)級(jí)的提升。

向量化執(zhí)行,可以簡(jiǎn)單地看作一項(xiàng)消除程序中循環(huán)的優(yōu)化。這里用一個(gè)形象的例子比喻。小胡經(jīng)營(yíng)了一家果汁店,雖然店里的鮮榨蘋果汁深受大家喜愛,但客戶總是抱怨制作果汁的速度太慢。小胡的店里只有一臺(tái)榨汁機(jī),每次他都會(huì)從籃子里拿出一個(gè)蘋果,放到榨汁機(jī)內(nèi)等待出汁。如果有8個(gè)客戶,每個(gè)客戶都點(diǎn)了一杯蘋果汁,那么小胡需要重復(fù)循環(huán)8次上述的榨汁流程,才能榨出8杯蘋果汁。如果制作一杯果汁需要5分鐘,那么全部制作完畢則需要40分鐘。為了提升果汁的制作速度,小胡想出了一個(gè)辦法。他將榨汁機(jī)的數(shù)量從1臺(tái)增加到了8臺(tái),這么一來(lái),他就可以從籃子里一次性拿出8個(gè)蘋果,分別放入8臺(tái)榨汁機(jī)同時(shí)榨汁。此時(shí),小胡只需要5分鐘就能夠制作出8杯蘋果汁。為了制作n杯果汁,非向量化執(zhí)行的方式是用1臺(tái)榨汁機(jī)重復(fù)循環(huán)制作n次,而向量化執(zhí)行的方式是用n臺(tái)榨汁機(jī)只執(zhí)行1次。

為了實(shí)現(xiàn)向量化執(zhí)行,需要利用CPU的SIMD指令。SIMD的全稱是Single Instruction Multiple Data,即用單條指令操作多條數(shù)據(jù)?,F(xiàn)代計(jì)算機(jī)系統(tǒng)概念中,它是通過數(shù)據(jù)并行以提高性能的一種實(shí)現(xiàn)方式 ( 其他的還有指令級(jí)并行和線程級(jí)并行 ),它的原理是在CPU寄存器層面實(shí)現(xiàn)數(shù)據(jù)的并行操作。

在計(jì)算機(jī)系統(tǒng)的體系結(jié)構(gòu)中,存儲(chǔ)系統(tǒng)是一種層次結(jié)構(gòu)。典型服務(wù)器計(jì)算機(jī)的存儲(chǔ)層次結(jié)構(gòu)如圖1所示。一個(gè)實(shí)用的經(jīng)驗(yàn)告訴我們,存儲(chǔ)媒介距離CPU越近,則訪問數(shù)據(jù)的速度越快。  

 

 

 

 

從上圖中可以看到,從左向右,距離CPU越遠(yuǎn),則數(shù)據(jù)的訪問速度越慢。從寄存器中訪問數(shù)據(jù)的速度,是從內(nèi)存訪問數(shù)據(jù)速度的300倍,是從磁盤中訪問數(shù)據(jù)速度的3000萬(wàn)倍。所以利用CPU向量化執(zhí)行的特性,對(duì)于程序的性能提升意義非凡。

ClickHouse目前利用SSE4.2指令集實(shí)現(xiàn)向量化執(zhí)行。

4.關(guān)系模型與SQL查詢

相比HBase和Redis這類NoSQL數(shù)據(jù)庫(kù),ClickHouse使用關(guān)系模型描述數(shù)據(jù)并提供了傳統(tǒng)數(shù)據(jù)庫(kù)的概念 ( 數(shù)據(jù)庫(kù)、表、視圖和函數(shù)等 )。與此同時(shí),ClickHouse完全使用SQL作為查詢語(yǔ)言 ( 支持GROUP BY、ORDER BY、JOIN、IN等大部分標(biāo)準(zhǔn)SQL ),這使得它平易近人,容易理解和學(xué)習(xí)。因?yàn)殛P(guān)系型數(shù)據(jù)庫(kù)和SQL語(yǔ)言,可以說是軟件領(lǐng)域發(fā)展至今應(yīng)用最為廣泛的技術(shù)之一,擁有極高的"群眾基礎(chǔ)"。也正因?yàn)镃lickHouse提供了標(biāo)準(zhǔn)協(xié)議的SQL查詢接口,使得現(xiàn)有的第三方分析可視化系統(tǒng)可以輕松與它集成對(duì)接。在SQL解析方面,ClickHouse是大小寫敏感的,這意味著SELECT a 和 SELECT A所代表的語(yǔ)義是不同的。

關(guān)系模型相比文檔和鍵值對(duì)等其他模型,擁有更好的描述能力,也能夠更加清晰地表述實(shí)體間的關(guān)系。更重要的是,在OLAP領(lǐng)域,已有的大量數(shù)據(jù)建模工作都是基于關(guān)系模型展開的 ( 星型模型、雪花模型乃至寬表模型 )。ClickHouse使用了關(guān)系模型,所以將構(gòu)建在傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)或數(shù)據(jù)倉(cāng)庫(kù)之上的系統(tǒng)遷移到ClickHouse的成本會(huì)變得更低,可以直接沿用之前的經(jīng)驗(yàn)成果。

5.多樣化的表引擎

也許因?yàn)閅andex.Metrica的最初架構(gòu)是基于MySQL實(shí)現(xiàn)的,所以在ClickHouse的設(shè)計(jì)中,能夠察覺到一些MySQL的影子,表引擎的設(shè)計(jì)就是其中之一。與MySQL類似,ClickHouse也將存儲(chǔ)部分進(jìn)行了抽象,把存儲(chǔ)引擎作為一層獨(dú)立的接口。截至本書完稿時(shí),ClickHouse共擁有合并樹、內(nèi)存、文件、接口和其他6大類20多種表引擎。其中每一種表引擎都有著各自的特點(diǎn),用戶可以根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景的要求,選擇合適的表引擎使用。

通常而言,一個(gè)通用系統(tǒng)意味著更廣泛的適用性,能夠適應(yīng)更多的場(chǎng)景。但通用的另一種解釋是平庸,因?yàn)樗鼰o(wú)法在所有場(chǎng)景內(nèi)都做到極致。

在軟件的世界中,并不會(huì)存在一個(gè)能夠適用任何場(chǎng)景的通用系統(tǒng),為了突出某項(xiàng)特性,勢(shì)必會(huì)在別處有所取舍。其實(shí)世間萬(wàn)物都遵循著這樣的道理,就像信天翁和蜂鳥,雖然都屬于鳥類,但它們各自的特點(diǎn)卻鑄就了完全不同的體貌特征。信天翁擅長(zhǎng)遠(yuǎn)距離飛行,環(huán)繞地球一周只需要1至2個(gè)月的時(shí)間。因?yàn)樗軌蜷L(zhǎng)時(shí)間處于滑行狀態(tài),5天才需要扇動(dòng)一次翅膀,心率能夠保持在每分鐘100至200次之間。而蜂鳥能夠垂直懸停飛行,每秒可以揮動(dòng)翅膀70~100次,飛行時(shí)的心率能夠達(dá)到每分鐘1000次。如果用數(shù)據(jù)庫(kù)的場(chǎng)景類比信天翁和蜂鳥的特點(diǎn),那么信天翁代表的可能是使用普通硬件就能實(shí)現(xiàn)高性能的設(shè)計(jì)思路,數(shù)據(jù)按粗粒度處理,通過批處理的方式執(zhí)行;而蜂鳥代表的可能是按細(xì)粒度處理數(shù)據(jù)的設(shè)計(jì)思路,需要高性能硬件的支持。

將表引擎獨(dú)立設(shè)計(jì)的好處是顯而易見的,通過特定的表引擎支撐特定的場(chǎng)景,十分靈活。對(duì)于簡(jiǎn)單的場(chǎng)景,可直接使用簡(jiǎn)單的引擎降低成本,而復(fù)雜的場(chǎng)景也有合適的選擇。

6.多線程與分布式

ClickHouse幾乎具備現(xiàn)代化高性能數(shù)據(jù)庫(kù)的所有典型特征,對(duì)于可以提升性能的手段可謂是一一用盡,對(duì)于多線程和分布式這類被廣泛使用的技術(shù),自然更是不在話下。

如果說向量化執(zhí)行是通過數(shù)據(jù)級(jí)并行的方式提升了性能,那么多線程處理就是通過線程級(jí)并行的方式實(shí)現(xiàn)了性能的提升。相比基于底層硬件實(shí)現(xiàn)的向量化執(zhí)行SIMD,線程級(jí)并行通常由更高層次的軟件層面控制?,F(xiàn)代計(jì)算機(jī)系統(tǒng)早已普及了多處理器架構(gòu),所以現(xiàn)今市面上的服務(wù)器都具備良好的多核心多線程處理能力。由于SIMD不適合用于帶有較多分支判斷的場(chǎng)景,ClickHouse也大量使用了多線程技術(shù)以實(shí)現(xiàn)提速,以此和向量化執(zhí)行形成互補(bǔ)。

如果一個(gè)籃子裝不下所有的雞蛋,那么就多用幾個(gè)籃子來(lái)裝,這就是分布式設(shè)計(jì)中分而治之的基本思想。同理,如果一臺(tái)服務(wù)器性能吃緊,那么就利用多臺(tái)服務(wù)的資源協(xié)同處理。為了實(shí)現(xiàn)這一目標(biāo),首先需要在數(shù)據(jù)層面實(shí)現(xiàn)數(shù)據(jù)的分布式。因?yàn)樵诜植际筋I(lǐng)域,存在一條金科玉律—計(jì)算移動(dòng)比數(shù)據(jù)移動(dòng)更加劃算。在各服務(wù)器之間,通過網(wǎng)絡(luò)傳輸數(shù)據(jù)的成本是高昂的,所以相比移動(dòng)數(shù)據(jù),更為聰明的做法是預(yù)先將數(shù)據(jù)分布到各臺(tái)服務(wù)器,將數(shù)據(jù)的計(jì)算查詢直接下推到數(shù)據(jù)所在的服務(wù)器。ClickHouse在數(shù)據(jù)存取方面,既支持分區(qū) ( 縱向擴(kuò)展,利用多線程原理 ),也支持分片 ( 橫向擴(kuò)展,利用分布式原理 ),可以說是將多線程和分布式的技術(shù)應(yīng)用到了極致。

7.多主架構(gòu)

HDFS、Spark、HBase和Elasticsearch這類分布式系統(tǒng),都采用了Master-Slave主從架構(gòu),由一個(gè)管控節(jié)點(diǎn)作為L(zhǎng)eader統(tǒng)籌全局。而ClickHouse則采用Multi-Master多主架構(gòu),集群中的每個(gè)節(jié)點(diǎn)角色對(duì)等,客戶端訪問任意一個(gè)節(jié)點(diǎn)都能得到相同的效果。這種多主的架構(gòu)有許多優(yōu)勢(shì),例如對(duì)等的角色使系統(tǒng)架構(gòu)變得更加簡(jiǎn)單,不用再區(qū)分主控節(jié)點(diǎn)、數(shù)據(jù)節(jié)點(diǎn)和計(jì)算節(jié)點(diǎn),集群中的所有節(jié)點(diǎn)功能相同。所以它天然規(guī)避了單點(diǎn)故障的問題,非常適合用于多數(shù)據(jù)中心、異地多活的場(chǎng)景。

8.在線查詢

ClickHouse經(jīng)常會(huì)被拿來(lái)與其他的分析型數(shù)據(jù)庫(kù)作對(duì)比,比如Vertica、SparkSQL、Hive和Elasticsearch等,它與這些數(shù)據(jù)庫(kù)確實(shí)存在許多相似之處。例如,它們都可以支撐海量數(shù)據(jù)的查詢場(chǎng)景,都擁有分布式架構(gòu),都支持列存、數(shù)據(jù)分片、計(jì)算下推等特性。這其實(shí)也側(cè)面說明了ClickHouse在設(shè)計(jì)上確實(shí)吸取了各路奇技淫巧。與其他數(shù)據(jù)庫(kù)相比,ClickHouse也擁有明顯的優(yōu)勢(shì)。例如,Vertica這類商用軟件價(jià)格高昂;SparkSQL與Hive這類系統(tǒng)無(wú)法保障90%的查詢?cè)?秒內(nèi)返回,在大數(shù)據(jù)量下的復(fù)雜查詢可能會(huì)需要分鐘級(jí)的響應(yīng)時(shí)間;而Elasticsearch這類搜索引擎在處理億級(jí)數(shù)據(jù)聚合查詢時(shí)則顯得捉襟見肘。

正如ClickHouse的"廣告詞"所言,其他的開源系統(tǒng)太慢,商用的系統(tǒng)太貴,只有Clickouse在成本與性能之間做到了良好平衡,即又快又開源。ClickHouse當(dāng)之無(wú)愧地闡釋了"在線"二字的含義,即便是在復(fù)雜查詢的場(chǎng)景下,它也能夠做到極快響應(yīng),且無(wú)須對(duì)數(shù)據(jù)進(jìn)行任何預(yù)處理加工。

9.數(shù)據(jù)分片與分布式查詢

數(shù)據(jù)分片是將數(shù)據(jù)進(jìn)行橫向切分,這是一種在面對(duì)海量數(shù)據(jù)的場(chǎng)景下,解決存儲(chǔ)和查詢瓶頸的有效手段,是一種分治思想的體現(xiàn)。ClickHouse支持分片,而分片則依賴集群。每個(gè)集群由1到多個(gè)分片組成,而每個(gè)分片則對(duì)應(yīng)了ClickHouse的1個(gè)服務(wù)節(jié)點(diǎn)。分片的數(shù)量上限取決于節(jié)點(diǎn)數(shù)量 ( 1個(gè)分片只能對(duì)應(yīng)1個(gè)服務(wù)節(jié)點(diǎn) )。

ClickHouse并不像其他分布式系統(tǒng)那樣,擁有高度自動(dòng)化的分片功能。ClickHouse提供了本地表 ( Local Table ) 與分布式表 ( Distributed Table ) 的概念。一張本地表等同于一份數(shù)據(jù)的分片。而分布式表本身不存儲(chǔ)任何數(shù)據(jù),它是本地表的訪問代理,其作用類似分庫(kù)中間件。借助分布式表,能夠代理訪問多個(gè)數(shù)據(jù)分片,從而實(shí)現(xiàn)分布式查詢。

這種設(shè)計(jì)類似數(shù)據(jù)庫(kù)的分庫(kù)和分表,十分靈活。例如在業(yè)務(wù)系統(tǒng)上線的初期,數(shù)據(jù)體量并不高,此時(shí)數(shù)據(jù)表并不需要多個(gè)分片。所以使用單個(gè)節(jié)點(diǎn)的本地表 ( 單個(gè)數(shù)據(jù)分片 ) 即可滿足業(yè)務(wù)需求,待到業(yè)務(wù)增長(zhǎng)、數(shù)據(jù)量增大的時(shí)候,再通過新增數(shù)據(jù)分片的方式分流數(shù)據(jù),并通過分布式表實(shí)現(xiàn)分布式查詢。這就好比一輛手動(dòng)擋賽車,它將所有的選擇權(quán)都交到了使用者的手中。

ClickHouse的架構(gòu)設(shè)計(jì)

目前ClickHouse公開的資料相對(duì)匱乏,比如在架構(gòu)設(shè)計(jì)層面就很難找到完整的資料,甚至連一張整體的架構(gòu)圖都沒有。我想這就是它為何身為一款開源軟件,但又顯得如此神秘的原因之一吧。即便如此,我們還是能從一些零散的材料中找到一些蛛絲馬跡。接下來(lái)會(huì)說明ClickHouse底層設(shè)計(jì)中的一些概念,這些概念可以幫助我們了解ClickHouse。 

 

 

1.Column與Field

Column和Field是ClickHouse數(shù)據(jù)最基礎(chǔ)的映射單元。作為一款百分之百的列式存儲(chǔ)數(shù)據(jù)庫(kù),ClickHouse按列存儲(chǔ)數(shù)據(jù),內(nèi)存中的一列數(shù)據(jù)由一個(gè)Column對(duì)象表示。Column對(duì)象分為接口和實(shí)現(xiàn)兩個(gè)部分,在IColumn接口對(duì)象中,定義了對(duì)數(shù)據(jù)進(jìn)行各種關(guān)系運(yùn)算的方法,例如插入數(shù)據(jù)的insertRangeFrom和insertFrom方法、用于分頁(yè)的cut,以及用于過濾的filter方法等。而這些方法的具體實(shí)現(xiàn)對(duì)象則根據(jù)數(shù)據(jù)類型的不同,由相應(yīng)的對(duì)象實(shí)現(xiàn),例如ColumnString、ColumnArray和ColumnTuple等。在大多數(shù)場(chǎng)合,ClickHouse都會(huì)以整列的方式操作數(shù)據(jù),但凡事也有例外。如果需要操作單個(gè)具體的數(shù)值 ( 也就是單列中的一行數(shù)據(jù) ),則需要使用Field對(duì)象,F(xiàn)ield對(duì)象代表一個(gè)單值。與Column對(duì)象的泛化設(shè)計(jì)思路不同,F(xiàn)ield對(duì)象使用了聚合的設(shè)計(jì)模式。在Field對(duì)象內(nèi)部聚合了Null、UInt64、String和Array等13種數(shù)據(jù)類型及相應(yīng)的處理邏輯。

2.DataType

數(shù)據(jù)的序列化和反序列化工作由DataType負(fù)責(zé)。IDataType接口定義了許多正反序列化的方法,它們成對(duì)出現(xiàn),例如serializeBinary和deserializeBinary、serializeTextJSON和deserializeTextJSON等,涵蓋了常用的二進(jìn)制、文本、JSON、XML、CSV和Protobuf等多種格式類型。IDataType也使用了泛化的設(shè)計(jì)模式,具體方法的實(shí)現(xiàn)邏輯由對(duì)應(yīng)數(shù)據(jù)類型的實(shí)例承載,例如DataTypeString、DataTypeArray及DataTypeTuple等。

DataType雖然負(fù)責(zé)序列化相關(guān)工作,但它并不直接負(fù)責(zé)數(shù)據(jù)的讀取,而是轉(zhuǎn)由從Column或Field對(duì)象獲取。在DataType的實(shí)現(xiàn)類中,聚合了相應(yīng)數(shù)據(jù)類型的Column對(duì)象和Field對(duì)象。例如,DataTypeString會(huì)引用字符串類型的ColumnString,而DataTypeArray則會(huì)引用數(shù)組類型的ColumnArray,以此類推。

3.Block與Block流

ClickHouse內(nèi)部的數(shù)據(jù)操作是面向Block對(duì)象進(jìn)行的,并且采用了流的形式。雖然Column和Filed組成了數(shù)據(jù)的基本映射單元,但對(duì)應(yīng)到實(shí)際操作,它們還缺少了一些必要的信息,比如數(shù)據(jù)的類型及列的名稱。于是ClickHouse設(shè)計(jì)了Block對(duì)象,Block對(duì)象可以看作數(shù)據(jù)表的子集。Block對(duì)象的本質(zhì)是由數(shù)據(jù)對(duì)象、數(shù)據(jù)類型和列名稱組成的三元組,即Column、DataType及列名稱字符串。Column提供了數(shù)據(jù)的讀取能力,而DataType知道如何正反序列化,所以Block在這些對(duì)象的基礎(chǔ)之上實(shí)現(xiàn)了進(jìn)一步的抽象和封裝,從而簡(jiǎn)化了整個(gè)使用的過程,僅通過Block對(duì)象就能完成一系列的數(shù)據(jù)操作。在具體的實(shí)現(xiàn)過程中,Block并沒有直接聚合Column和DataType對(duì)象,而是通過ColumnWithTypeAndName對(duì)象進(jìn)行間接引用。

有了Block對(duì)象這一層封裝之后,對(duì)Block流的設(shè)計(jì)就是水到渠成的事情了。流操作有兩組頂層接口:IBlockInputStream負(fù)責(zé)數(shù)據(jù)的讀取和關(guān)系運(yùn)算,IBlockOutputStream負(fù)責(zé)將數(shù)據(jù)輸出到下一環(huán)節(jié)。Block流也使用了泛化的設(shè)計(jì)模式,對(duì)數(shù)據(jù)的各種操作最終都會(huì)轉(zhuǎn)換成其中一種流的實(shí)現(xiàn)。IBlockInputStream接口定義了讀取數(shù)據(jù)的若干個(gè)read虛方法,而具體的實(shí)現(xiàn)邏輯則交由它的實(shí)現(xiàn)類來(lái)填充。

IBlockInputStream接口總共有60多個(gè)實(shí)現(xiàn)類,它們涵蓋了ClickHouse數(shù)據(jù)攝取的方方面面。這些實(shí)現(xiàn)類大致可以分為三類:第一類用于處理數(shù)據(jù)定義的DDL操作,例如DDLQueryStatusInputStream等;第二類用于處理關(guān)系運(yùn)算的相關(guān)操作,例如LimitBlockInput-Stream、JoinBlockInputStream及AggregatingBlockInputStream等;第三類則是與表引擎呼應(yīng),每一種表引擎都擁有與之對(duì)應(yīng)的BlockInputStream實(shí)現(xiàn),例如MergeTreeBaseSelect-BlockInputStream ( MergeTree表引擎 )、TinyLogBlockInputStream ( TinyLog表引擎 ) 及KafkaBlockInputStream ( Kafka表引擎 ) 等。

IBlockOutputStream的設(shè)計(jì)與IBlockInputStream如出一轍。IBlockOutputStream接口同樣也定義了若干寫入數(shù)據(jù)的write虛方法。它的實(shí)現(xiàn)類比IBlockInputStream要少許多,一共只有20多種。這些實(shí)現(xiàn)類基本用于表引擎的相關(guān)處理,負(fù)責(zé)將數(shù)據(jù)寫入下一環(huán)節(jié)或者最終目的地,例如MergeTreeBlockOutputStream 、TinyLogBlockOutputStream及StorageFileBlock-OutputStream等。

4.Table

在數(shù)據(jù)表的底層設(shè)計(jì)中并沒有所謂的Table對(duì)象,它直接使用IStorage接口指代數(shù)據(jù)表。表引擎是ClickHouse的一個(gè)顯著特性,不同的表引擎由不同的子類實(shí)現(xiàn),例如IStorageSystemOneBlock ( 系統(tǒng)表 )、StorageMergeTree ( 合并樹表引擎 ) 和StorageTinyLog ( 日志表引擎 ) 等。IStorage接口定義了DDL ( 如ALTER、RENAME、OPTIMIZE和DROP等 ) 、read和write方法,它們分別負(fù)責(zé)數(shù)據(jù)的定義、查詢與寫入。在數(shù)據(jù)查詢時(shí),IStorage負(fù)責(zé)根據(jù)AST查詢語(yǔ)句的指示要求,返回指定列的原始數(shù)據(jù)。后續(xù)對(duì)數(shù)據(jù)的進(jìn)一步加工、計(jì)算和過濾,則會(huì)統(tǒng)一交由Interpreter解釋器對(duì)象處理。對(duì)Table發(fā)起的一次操作通常都會(huì)經(jīng)歷這樣的過程,接收AST查詢語(yǔ)句,根據(jù)AST返回指定列的數(shù)據(jù),之后再將數(shù)據(jù)交由Interpreter做進(jìn)一步處理。

5.Parser與Interpreter

Parser和Interpreter是非常重要的兩組接口:Parser分析器負(fù)責(zé)創(chuàng)建AST對(duì)象;而Interpreter解釋器則負(fù)責(zé)解釋AST,并進(jìn)一步創(chuàng)建查詢的執(zhí)行管道。它們與IStorage一起,串聯(lián)起了整個(gè)數(shù)據(jù)查詢的過程。Parser分析器可以將一條SQL語(yǔ)句以遞歸下降的方法解析成AST語(yǔ)法樹的形式。不同的SQL語(yǔ)句,會(huì)經(jīng)由不同的Parser實(shí)現(xiàn)類解析。例如,有負(fù)責(zé)解析DDL查詢語(yǔ)句的ParserRenameQuery、ParserDropQuery和ParserAlterQuery解析器,也有負(fù)責(zé)解析INSERT語(yǔ)句的ParserInsertQuery解析器,還有負(fù)責(zé)SELECT語(yǔ)句的ParserSelectQuery等。

Interpreter解釋器的作用就像Service服務(wù)層一樣,起到串聯(lián)整個(gè)查詢過程的作用,它會(huì)根據(jù)解釋器的類型,聚合它所需要的資源。首先它會(huì)解析AST對(duì)象;然后執(zhí)行"業(yè)務(wù)邏輯" ( 例如分支判斷、設(shè)置參數(shù)、調(diào)用接口等 );最終返回IBlock對(duì)象,以線程的形式建立起一個(gè)查詢執(zhí)行管道。

6.Functions 與Aggregate Functions

ClickHouse主要提供兩類函數(shù)—普通函數(shù)和聚合函數(shù)。普通函數(shù)由IFunction接口定義,擁有數(shù)十種函數(shù)實(shí)現(xiàn),例如FunctionFormatDateTime、FunctionSubstring等。除了一些常見的函數(shù) ( 諸如四則運(yùn)算、日期轉(zhuǎn)換等 ) 之外,也不乏一些非常實(shí)用的函數(shù),例如網(wǎng)址提取函數(shù)、IP地址脫敏函數(shù)等。普通函數(shù)是沒有狀態(tài)的,函數(shù)效果作用于每行數(shù)據(jù)之上。當(dāng)然,在函數(shù)具體執(zhí)行的過程中,并不會(huì)一行一行地運(yùn)算,而是采用向量化的方式直接作用于一整列數(shù)據(jù)。

聚合函數(shù)由IAggregateFunction接口定義,相比無(wú)狀態(tài)的普通函數(shù),聚合函數(shù)是有狀態(tài)的。以COUNT聚合函數(shù)為例,其AggregateFunctionCount的狀態(tài)使用整型UInt64記錄。聚合函數(shù)的狀態(tài)支持序列化與反序列化,所以能夠在分布式節(jié)點(diǎn)之間進(jìn)行傳輸,以實(shí)現(xiàn)增量計(jì)算。

7.Cluster與Replication

ClickHouse的集群由分片 ( Shard ) 組成,而每個(gè)分片又通過副本 ( Replica ) 組成。這種分層的概念,在一些流行的分布式系統(tǒng)中十分普遍。例如,在Elasticsearch的概念中,一個(gè)索引由分片和副本組成,副本可以看作一種特殊的分片。如果一個(gè)索引由5個(gè)分片組成,副本的基數(shù)是1,那么這個(gè)索引一共會(huì)擁有10個(gè)分片 ( 每1個(gè)分片對(duì)應(yīng)1個(gè)副本 )。

如果你用同樣的思路來(lái)理解ClickHouse的分片,那么很可能會(huì)在這里栽個(gè)跟頭。ClickHouse的某些設(shè)計(jì)總是顯得獨(dú)樹一幟,而集群與分片就是其中之一。這里有幾個(gè)與眾不同的特性。

  • ClickHouse的1個(gè)節(jié)點(diǎn)只能擁有1個(gè)分片,也就是說如果要實(shí)現(xiàn)1分片、1副本,則至少需要部署2個(gè)服務(wù)節(jié)點(diǎn)。

  • 分片只是一個(gè)邏輯概念,其物理承載還是由副本承擔(dān)的。

代碼清單1所示是ClickHouse的一份集群配置示例,從字面含義理解這份配置的語(yǔ)義,可以理解為自定義集群ch_cluster擁有1個(gè)shard ( 分片 ) 和1個(gè)replica ( 副本 ),且該副本由10.37.129.6服務(wù)節(jié)點(diǎn)承載。

 

 

 

從本質(zhì)上看,這組1分片、1副本的配置在ClickHouse中只有1個(gè)物理副本,所以它正確的語(yǔ)義應(yīng)該是1分片、0副本。分片更像是邏輯層的分組,在物理存儲(chǔ)層面則統(tǒng)一使用副本代表分片和副本。所以真正表示1分片、1副本語(yǔ)義的配置,應(yīng)該改為1個(gè)分片和2個(gè)副本,如代碼清單2所示。

 

 

 

ClickHouse為何如此之快

很多用戶心中一直會(huì)有這樣的疑問,為什么ClickHouse這么快?前面的介紹對(duì)這個(gè)問題已經(jīng)做出了科學(xué)合理的解釋。比方說,因?yàn)镃lickHouse是列式存儲(chǔ)數(shù)據(jù)庫(kù),所以快;也因?yàn)镃lickHouse使用了向量化引擎,所以快。這些解釋都站得住腳,但是依然不能消除全部的疑問。因?yàn)檫@些技術(shù)并不是秘密,世面上有很多數(shù)據(jù)庫(kù)同樣使用了這些技術(shù),但是依然沒有ClickHouse這么快。所以我想從另外一個(gè)角度來(lái)探討一番ClickHouse的秘訣到底是什么。

首先向各位讀者拋出一個(gè)疑問:在設(shè)計(jì)軟件架構(gòu)的時(shí)候,做設(shè)計(jì)的原則應(yīng)該是自頂向下地去設(shè)計(jì),還是應(yīng)該自下而上地去設(shè)計(jì)呢?在傳統(tǒng)觀念中,或者說在我的觀念中,自然是自頂向下的設(shè)計(jì),通常我們都被教導(dǎo)要做好頂層設(shè)計(jì)。而ClickHouse的設(shè)計(jì)則采用了自下而上的方式。ClickHouse的原型系統(tǒng)早在2008年就誕生了,在誕生之初它并沒有宏偉的規(guī)劃。相反它的目的很單純,就是希望能以最快的速度進(jìn)行GROUP BY查詢和過濾。他們是如何實(shí)踐自下而上設(shè)計(jì)的呢?

1.著眼硬件,先想后做

首先從硬件功能層面著手設(shè)計(jì),在設(shè)計(jì)伊始就至少需要想清楚如下幾個(gè)問題。

  • 我們將要使用的硬件水平是怎樣的?包括CPU、內(nèi)存、硬盤、網(wǎng)絡(luò)等。

  • 在這樣的硬件上,我們需要達(dá)到怎樣的性能?包括延遲、吞吐量等。

  • 我們準(zhǔn)備使用怎樣的數(shù)據(jù)結(jié)構(gòu)?包括String、HashTable、Vector等。

  • 選擇的這些數(shù)據(jù)結(jié)構(gòu),在我們的硬件上會(huì)如何工作?

如果能想清楚上面這些問題,那么在動(dòng)手實(shí)現(xiàn)功能之前,就已經(jīng)能夠計(jì)算出粗略的性能了。所以,基于將硬件功效最大化的目的,ClickHouse會(huì)在內(nèi)存中進(jìn)行GROUP BY,并且使用HashTable裝載數(shù)據(jù)。與此同時(shí),他們非常在意CPU L3級(jí)別的緩存,因?yàn)橐淮蜭3的緩存失效會(huì)帶來(lái)70~100ns的延遲。這意味著在單核CPU上,它會(huì)浪費(fèi)4000萬(wàn)次/秒的運(yùn)算;而在一個(gè)32線程的CPU上,則可能會(huì)浪費(fèi)5億次/秒的運(yùn)算。所以別小看這些細(xì)節(jié),一點(diǎn)一滴地將它們累加起來(lái),數(shù)據(jù)是非常可觀的。正因?yàn)樽⒁饬诉@些細(xì)節(jié),所以ClickHouse在基準(zhǔn)查詢中能做到1.75億次/秒的數(shù)據(jù)掃描性能。

2.算法在前,抽象在后

常有人念叨:"有時(shí)候,選擇比努力更重要。"確實(shí),路線選錯(cuò)了再努力也是白搭。在ClickHouse的底層實(shí)現(xiàn)中,經(jīng)常會(huì)面對(duì)一些重復(fù)的場(chǎng)景,例如字符串子串查詢、數(shù)組排序、使用HashTable等。如何才能實(shí)現(xiàn)性能的最大化呢?算法的選擇是重中之重。以字符串為例,有一本專門講解字符串搜索的書,名為"Handbook of Exact String Matching Algorithms",列舉了35種常見的字符串搜索算法。各位猜一猜ClickHouse使用了其中的哪一種?答案是一種都沒有。這是為什么呢?因?yàn)樾阅懿粔蚩臁T谧址阉鞣矫?,針?duì)不同的場(chǎng)景,ClickHouse最終選擇了這些算法:對(duì)于常量,使用Volnitsky算法;對(duì)于非常量,使用CPU的向量化執(zhí)行SIMD,暴力優(yōu)化;正則匹配使用re2和hyperscan算法。性能是算法選擇的首要考量指標(biāo)。

3.勇于嘗鮮,不行就換

除了字符串之外,其余的場(chǎng)景也與它類似,ClickHouse會(huì)使用最合適、最快的算法。如果世面上出現(xiàn)了號(hào)稱性能強(qiáng)大的新算法,ClickHouse團(tuán)隊(duì)會(huì)立即將其納入并進(jìn)行驗(yàn)證。如果效果不錯(cuò),就保留使用;如果性能不盡人意,就將其拋棄。

4.特定場(chǎng)景,特殊優(yōu)化

針對(duì)同一個(gè)場(chǎng)景的不同狀況,選擇使用不同的實(shí)現(xiàn)方式,盡可能將性能最大化。關(guān)于這一點(diǎn),其實(shí)在前面介紹字符串查詢時(shí),針對(duì)不同場(chǎng)景選擇不同算法的思路就有體現(xiàn)了。類似的例子還有很多,例如去重計(jì)數(shù)uniqCombined函數(shù),會(huì)根據(jù)數(shù)據(jù)量的不同選擇不同的算法:當(dāng)數(shù)據(jù)量較小的時(shí)候,會(huì)選擇Array保存;當(dāng)數(shù)據(jù)量中等的時(shí)候,會(huì)選擇HashSet;而當(dāng)數(shù)據(jù)量很大的時(shí)候,則使用HyperLogLog算法。

對(duì)于數(shù)據(jù)結(jié)構(gòu)比較清晰的場(chǎng)景,會(huì)通過代碼生成技術(shù)實(shí)現(xiàn)循環(huán)展開,以減少循環(huán)次數(shù)。接著就是大家熟知的大殺器—向量化執(zhí)行了。SIMD被廣泛地應(yīng)用于文本轉(zhuǎn)換、數(shù)據(jù)過濾、數(shù)據(jù)解壓和JSON轉(zhuǎn)換等場(chǎng)景。相較于單純地使用CPU,利用寄存器暴力優(yōu)化也算是一種降維打擊了。

5.持續(xù)測(cè)試,持續(xù)改進(jìn)

如果只是單純地在上述細(xì)節(jié)上下功夫,還不足以構(gòu)建出如此強(qiáng)大的ClickHouse,還需要擁有一個(gè)能夠持續(xù)驗(yàn)證、持續(xù)改進(jìn)的機(jī)制。由于Yandex的天然優(yōu)勢(shì),ClickHouse經(jīng)常會(huì)使用真實(shí)的數(shù)據(jù)進(jìn)行測(cè)試,這一點(diǎn)很好地保證了測(cè)試場(chǎng)景的真實(shí)性。與此同時(shí),ClickHouse也是我見過的發(fā)版速度最快的開源軟件了,差不多每個(gè)月都能發(fā)布一個(gè)版本。沒有一個(gè)可靠的持續(xù)集成環(huán)境,這一點(diǎn)是做不到的。正因?yàn)閾碛羞@樣的發(fā)版頻率,ClickHouse才能夠快速迭代、快速改進(jìn)。

所以ClickHouse的黑魔法并不是一項(xiàng)單一的技術(shù),而是一種自底向上的、追求極致性能的設(shè)計(jì)思路。這就是它如此之快的秘訣。

小結(jié)

本文我們快速瀏覽了世界第三大Web流量分析平臺(tái)Yandex.Metrica背后的支柱ClickHouse的核心特性和邏輯架構(gòu)。通過對(duì)核心特性部分的展示,ClickHouse如此強(qiáng)悍的緣由已初見端倪,列式存儲(chǔ)、向量化執(zhí)行引擎和表引擎都是它的撒手锏。

在架構(gòu)設(shè)計(jì)部分,則進(jìn)一步展示了ClickHouse的一些設(shè)計(jì)思路,例如Column、Field、Block和Cluster。了解這些設(shè)計(jì)思路,能夠幫助我們更好地理解和使用ClickHouse。最后又從另外一個(gè)角度探討了ClickHouse如此之快的秘訣。

 

責(zé)任編輯:張燕妮 來(lái)源: 民工哥技術(shù)之路
相關(guān)推薦

2021-07-28 14:20:13

正則PythonFlashText

2019-10-14 09:50:52

KeyDBRedis中間件

2023-05-30 16:34:42

AI

2020-03-04 13:55:28

c3p0數(shù)據(jù)庫(kù)連接池

2022-11-02 08:12:47

TurbopackVite

2022-10-27 08:31:31

架構(gòu)

2024-03-26 10:13:54

日志引擎SigLens

2023-04-07 08:17:39

fasthttp場(chǎng)景設(shè)計(jì)HTTP

2025-03-18 12:30:00

RubyJava語(yǔ)言

2017-09-06 11:18:14

2020-10-27 09:18:16

ClickHouse數(shù)據(jù)庫(kù)架構(gòu)

2015-11-25 14:39:51

LiFiWiFi

2023-03-07 08:34:01

2011-06-29 09:31:58

3G4G5G

2023-10-25 18:53:45

芯片AI芯片

2024-01-23 11:28:14

Eslint前端Oxlint

2023-09-08 15:05:51

Mojo編程語(yǔ)言

2021-05-06 10:52:09

Java Spring Bo框架

2015-01-16 10:43:09

WiGigWiFi

2014-09-16 10:52:38

瀏覽器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)