近期崛起的OLAP數據庫--ClickHouse

0 - 前言

ClickHouse是近期備受關注的開源列式存儲數據庫,主要用于數據分析(OLAP)領域。目前國內社區(qū)火熱,各大廠紛紛跟進大規(guī)模使用:

  • 今日頭條內部用來做用戶行為分析,內部一共幾千個ClickHouse節(jié)點,單機群最大1200節(jié)點,總數據量幾十PB,日增原始數據300TB左右。
  • 騰訊內部用ClickHouse做游戲數據分析,并且為之建立了一整套監(jiān)控運維體系。
  • 攜程內部從18年7月份開始接入試用,目前80%的業(yè)務都跑在ClickHouse上。每天數據增量十多億,近百萬次查詢請求。
  • 快手內部也在使用ClickHouse,存儲總量大約10PB, 每天新增200TB, 90%查詢小于3S。

在國外,Yandex內部有數百節(jié)點用于做用戶點擊行為分析,CloudFlare、Spotify等頭部公司也在使用。

在社區(qū)方面,github star數目增速驚人。

在DB-engines排名上,如下圖中紅色曲線所示。ClickHouse開源時間雖短,但是增勢迅猛。

1 - OLAP場景的特點

讀多于寫
不同于事務處理(OLTP)的場景,比如電商場景中加購物車、下單、支付等需要在原地進行大量insert、update、delete操作,數據分析(OLAP)場景通常是將數據批量導入后,進行任意維度的靈活探索、BI工具洞察、報表制作等。數據一次性寫入后,分析師需要嘗試從各個角度對數據做挖掘、分析,直到發(fā)現其中的商業(yè)價值、業(yè)務變化趨勢等信息。這是一個需要反復試錯、不斷調整、持續(xù)優(yōu)化的過程,其中數據的讀取次數遠多于寫入次數。這就要求底層數據庫為這個特點做專門設計,而不是盲目采用傳統(tǒng)數據庫的技術架構。

大寬表,讀大量行但是少量列,結果集較小
在OLAP場景中,通常存在一張或是幾張多列的大寬表,列數高達數百甚至數千列。對數據分析處理時,選擇其中的少數幾列作為維度列、其他少數幾列作為指標列,然后對全表或某一個較大范圍內的數據做聚合計算。這個過程會掃描大量的行數據,但是只用到了其中的少數列。而聚合計算的結果集相比于動輒數十億的原始數據,也明顯小得多。

數據批量寫入,且數據不更新或少更新
OLTP類業(yè)務對于延時(Latency)要求更高,要避免讓客戶等待造成業(yè)務損失;而OLAP類業(yè)務,由于數據量非常大,通常更加關注寫入吞吐(Throughput),要求海量數據能夠盡快導入完成。一旦導入完成,歷史數據往往作為存檔,不會再做更新、刪除操作。

無需事務,數據一致性要求低
OLAP類業(yè)務對于事務需求較少,通常是導入歷史日志數據,或搭配一款事務型數據庫并實時從事務型數據庫中進行數據同步。多數OLAP系統(tǒng)都支持最終一致性。

靈活多變,不適合預先建模
分析場景下,隨著業(yè)務變化要及時調整分析維度、挖掘方法,以盡快發(fā)現數據價值、更新業(yè)務指標。而數據倉庫中通常存儲著海量的歷史數據,調整代價十分高昂。預先建模技術雖然可以在特定場景中加速計算,但是無法滿足業(yè)務靈活多變的發(fā)展需求,維護成本過高。

2 - ClickHouse存儲層

ClickHouse從OLAP場景需求出發(fā),定制開發(fā)了一套全新的高效列式存儲引擎,并且實現了數據有序存儲、主鍵索引、稀疏索引、數據Sharding、數據Partitioning、TTL、主備復制等豐富功能。以上功能共同為ClickHouse極速的分析性能奠定了基礎。

列式存儲
與行存將每一行的數據連續(xù)存儲不同,列存將每一列的數據連續(xù)存儲。示例圖如下:

行存和列存

相比于行式存儲,列式存儲在分析場景下有著許多優(yōu)良的特性:

  1. 如前所述,分析場景中往往需要讀大量行但是少數幾個列。在行存模式下,數據按行連續(xù)存儲,所有列的數據都存儲在一個block中,不參與計算的列在IO時也要全部讀出,讀取操作被嚴重放大。而列存模式下,只需要讀取參與計算的列即可,極大的減低了IO cost,加速了查詢。
  2. 同一列中的數據屬于同一類型,壓縮效果顯著。列存往往有著高達十倍甚至更高的壓縮比,節(jié)省了大量的存儲空間,降低了存儲成本。
  3. 更高的壓縮比意味著更小的data size,從磁盤中讀取相應數據耗時更短。
  4. 自由的壓縮算法選擇。不同列的數據具有不同的數據類型,適用的壓縮算法也就不盡相同。可以針對不同列類型,選擇最合適的壓縮算法。
  5. 高壓縮比,意味著同等大小的內存能夠存放更多數據,系統(tǒng)cache效果更好。

官方數據顯示,通過使用列存,在某些分析場景下,能夠獲得100倍甚至更高的加速效應。

數據有序存儲
ClickHouse支持在建表時,指定將數據按照某些列進行sort by。

排序后,保證了相同sort key的數據在磁盤上連續(xù)存儲,且有序擺放。在進行等值、范圍查詢時,where條件命中的數據都緊密存儲在一個或若干個連續(xù)的Block中,而不是分散的存儲在任意多個Block, 大幅減少需要IO的block數量。另外,連續(xù)IO也能夠充分利用操作系統(tǒng)page cache的預取能力,減少page fault。

主鍵索引
ClickHouse支持主鍵索引,它將每列數據按照index granularity(默認8192行)進行劃分,每個index granularity的開頭第一行被稱為一個mark行。主鍵索引存儲該mark行對應的primary key的值。

對于where條件中含有primary key的查詢,通過對主鍵索引進行二分查找,能夠直接定位到對應的index granularity,避免了全表掃描從而加速查詢。

但是值得注意的是:ClickHouse的主鍵索引與MySQL等數據庫不同,它并不用于去重,即便primary key相同的行,也可以同時存在于數據庫中。

稀疏索引
ClickHouse支持對任意列創(chuàng)建任意數量的稀疏索引。其中被索引的value可以是任意的合法SQL Expression,并不僅僅局限于對column value本身進行索引。之所以叫稀疏索引,是因為它本質上是對一個完整index granularity(默認8192行)的統(tǒng)計信息,并不會具體記錄每一行在文件中的位置。目前支持的稀疏索引類型包括:

  • minmax: 以index granularity為單位,存儲指定表達式計算后的min、max值;在等值和范圍查詢中能夠幫助快速跳過不滿足要求的塊,減少IO。
  • set(max_rows):以index granularity為單位,存儲指定表達式的distinct value集合,用于快速判斷等值查詢是否命中該塊,減少IO。
  • ngrambf_v1(n, size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed):將string進行ngram分詞后,構建bloom filter,能夠優(yōu)化等值、like、in等查詢條件。
  • tokenbf_v1(size_of_bloom_filter_in_bytes, number_of_hash_functions, random_seed): 與ngrambf_v1類似,區(qū)別是不使用ngram進行分詞,而是通過標點符號進行詞語分割。
  • bloom_filter([false_positive]):對指定列構建bloom filter,用于加速等值、like、in等查詢條件的執(zhí)行。

數據Sharding
ClickHouse支持單機模式,也支持分布式集群模式。在分布式模式下,ClickHouse會將數據分為多個分片,并且分布到不同節(jié)點上。不同的分片策略在應對不同的SQL Pattern時,各有優(yōu)勢。ClickHouse提供了豐富的sharding策略,讓業(yè)務可以根據實際需求選用。

  • random隨機分片:寫入數據會被隨機分發(fā)到分布式集群中的某個節(jié)點上。
  • constant固定分片:寫入數據會被分發(fā)到固定一個節(jié)點上。
  • column value分片:按照某一列的值進行hash分片。
  • 自定義表達式分片:指定任意合法表達式,根據表達式被計算后的值進行hash分片。

數據分片,讓ClickHouse可以充分利用整個集群的大規(guī)模并行計算能力,快速返回查詢結果。

更重要的是,多樣化的分片功能,為業(yè)務優(yōu)化打開了想象空間。比如在hash sharding的情況下,JOIN計算能夠避免數據shuffle,直接在本地進行l(wèi)ocal join; 支持自定義sharding,可以為不同業(yè)務和SQL Pattern定制最適合的分片策略;利用自定義sharding功能,通過設置合理的sharding expression可以解決分片間數據傾斜問題等。

另外,sharding機制使得ClickHouse可以橫向線性拓展,構建大規(guī)模分布式集群,從而具備處理海量數據的能力。

數據Partitioning
ClickHouse支持PARTITION BY子句,在建表時可以指定按照任意合法表達式進行數據分區(qū)操作,比如通過toYYYYMM()將數據按月進行分區(qū)、toMonday()將數據按照周幾進行分區(qū)、對Enum類型的列直接每種取值作為一個分區(qū)等。

數據Partition在ClickHouse中主要有兩方面應用:

  • 在partition key上進行分區(qū)裁剪,只查詢必要的數據。靈活的partition expression設置,使得可以根據SQL Pattern進行分區(qū)設置,最大化的貼合業(yè)務特點。
  • 對partition進行TTL管理,淘汰過期的分區(qū)數據。

數據TTL
在分析場景中,數據的價值隨著時間流逝而不斷降低,多數業(yè)務出于成本考慮只會保留最近幾個月的數據,ClickHouse通過TTL提供了數據生命周期管理的能力。

ClickHouse支持幾種不同粒度的TTL:

  • 列級別TTL:當一列中的部分數據過期后,會被替換成默認值;當全列數據都過期后,會刪除該列。
  • 行級別TTL:當某一行過期后,會直接刪除該行。
  • 分區(qū)級別TTL:當分區(qū)過期后,會直接刪除該分區(qū)。

高吞吐寫入能力
ClickHouse采用類LSM Tree的結構,數據寫入后定期在后臺Compaction。通過類LSM tree的結構,ClickHouse在數據導入時全部是順序append寫,寫入后數據段不可更改,在后臺compaction時也是多個段merge sort后順序寫回磁盤。順序寫的特性,充分利用了磁盤的吞吐能力,即便在HDD上也有著優(yōu)異的寫入性能。

官方公開benchmark測試顯示能夠達到50MB-200MB/s的寫入吞吐能力,按照每行100Byte估算,大約相當于50W-200W條/s的寫入速度。

有限支持delete、update
在分析場景中,刪除、更新操作并不是核心需求。ClickHouse沒有直接支持delete、update操作,而是變相支持了mutation操作,語法為alter table delete where filter_expr,alter table update col=val where filter_expr。

目前主要限制為刪除、更新操作為異步操作,需要后臺compation之后才能生效。

主備同步
ClickHouse通過主備復制提供了高可用能力,主備架構下支持無縫升級等運維操作。而且相比于其他系統(tǒng)它的實現有著自己的特色:

  • 默認配置下,任何副本都處于active模式,可以對外提供查詢服務;
  • 可以任意配置副本個數,副本數量可以從0個到任意多個;
  • 不同shard可以配置不提供副本個數,用于解決單個shard的查詢熱點問題;

3 - ClickHouse計算層

ClickHouse在計算層做了非常細致的工作,竭盡所能榨干硬件能力,提升查詢速度。它實現了單機多核并行、分布式計算、向量化執(zhí)行與SIMD指令、代碼生成等多種重要技術。

多核并行
ClickHouse將數據劃分為多個partition,每個partition再進一步劃分為多個index granularity,然后通過多個CPU核心分別處理其中的一部分來實現并行數據處理。

在這種設計下,單條Query就能利用整機所有CPU。極致的并行處理能力,極大的降低了查詢延時。

分布式計算
除了優(yōu)秀的單機并行處理能力,ClickHouse還提供了可線性拓展的分布式計算能力。ClickHouse會自動將查詢拆解為多個task下發(fā)到集群中,然后進行多機并行處理,最后把結果匯聚到一起。

在存在多副本的情況下,ClickHouse提供了多種query下發(fā)策略:

  • 隨機下發(fā):在多個replica中隨機選擇一個;
  • 最近hostname原則:選擇與當前下發(fā)機器最相近的hostname節(jié)點,進行query下發(fā)。在特定的網絡拓撲下,可以降低網絡延時。而且能夠確保query下發(fā)到固定的replica機器,充分利用系統(tǒng)cache。
  • in order:按照特定順序逐個嘗試下發(fā),當前一個replica不可用時,順延到下一個replica;
  • first or random:在In Order模式下,當第一個replica不可用時,所有workload都會積壓到第二個Replica,導致負載不均衡。first or random解決了這個問題:當第一個replica不可用時,隨機選擇一個其他replica,從而保證其余replica間負載均衡。另外在跨region復制場景下,通過設置第一個replica為本region內的副本,可以顯著降低網絡延時;

向量化執(zhí)行與SIMD
ClickHouse不僅將數據按列存儲,而且按列進行計算。傳統(tǒng)OLTP數據庫通常采用按行計算,原因是事務處理中以點查為主,SQL計算量小,實現這些技術的收益不夠明顯。但是在分析場景下,單個SQL所涉及計算量可能極大,將每行作為一個基本單元進行處理會帶來嚴重的性能損耗:

  • 對每一行數據都要調用相應的函數,函數調用開銷占比高;
  • 存儲層按列存儲數據,在內存中也按列組織,但是計算層按行處理,無法充分利用CPU cache的預讀能力,造成CPU Cache miss嚴重;
  • 按行處理,無法利用高效的SIMD指令;

ClickHouse實現了向量執(zhí)行引擎(Vectorized execution engine),對內存中的列式數據,一個batch調用一次SIMD指令(而非每一行調用一次),不僅減少了函數調用次數、降低了cache miss,而且可以充分發(fā)揮SIMD指令的并行能力,大幅縮短了計算耗時。向量執(zhí)行引擎,通常能夠帶來數倍的性能提升。

動態(tài)代碼生成Runtime Codegen
在經典的數據庫實現中,通常對表達式計算采用火山模型,也即將查詢轉換成一個個operator,比如HashJoin、Scan、IndexScan、Aggregation等。為了連接不同算子,operator之間采用統(tǒng)一的接口,比如open/next/close。在每個算子內部都實現了父類的這些虛函數,在分析場景中單條SQL要處理數據通常高達數億行,虛函數的調用開銷不再可以忽略不計。另外,在每個算子內部都要考慮多種變量,比如列類型、列的size、列的個數等,存在著大量的if-else分支判斷導致CPU分支預測失效。

ClickHouse實現了Expression級別的runtime codegen,動態(tài)地根據當前SQL直接生成代碼,然后編譯執(zhí)行。如下圖例子所示,對于Expression直接生成代碼,不僅消除了大量的虛函數調用(即圖中多個function pointer的調用),而且由于在運行時表達式的參數類型、個數等都是已知的,也消除了不必要的if-else分支判斷。

Runtime Codegen

近似計算
近似計算以損失一定結果精度為代價,極大地提升查詢性能。在海量數據處理中,近似計算價值更加明顯。

ClickHouse實現了多種近似計算功能:

  • 近似估算distinct values、中位數,分位數等多種聚合函數;
  • 建表DDL支持SAMPLE BY子句,支持對于數據進行抽樣處理;

復雜數據類型支持
ClickHouse還提供了array、json、tuple、set等復合數據類型,支持業(yè)務schema的靈活變更。

4 - 結語

近年來ClickHouse發(fā)展趨勢迅猛,社區(qū)和大廠都紛紛跟進使用。本文嘗試從OLAP場景的需求出發(fā),介紹了ClickHouse存儲層、計算層的主要設計。ClickHouse實現了大多數當前主流的數據分析技術,具有明顯的技術優(yōu)勢:

  • 提供了極致的查詢性能:開源公開benchmark顯示比傳統(tǒng)方法快1001000倍,提供50MB200MB/s的高吞吐實時導入能力);
  • 以極低的成本存儲海量數據: 借助于精心設計的列存、高效的數據壓縮算法,提供高達10倍的壓縮比,大幅提升單機數據存儲和計算能力,大幅降低使用成本,是構建海量數據倉庫的絕佳方案。
  • 簡單靈活又不失強大:提供完善SQL支持,上手十分簡單;提供json、map、array等靈活數據類型適配業(yè)務快速變化;同時支持近似計算、概率數據結構等應對海量數據處理。

相比于開源社區(qū)的其他幾項分析型技術,如Druid、Presto、Impala、Kylin、ElasticSearch等,ClickHouse更是一整套完善的解決方案,它自包含了存儲和計算能力(無需額外依賴其他存儲組件),完全自主實現了高可用,而且支持完整的SQL語法包括JOIN等,技術上有著明顯優(yōu)勢。相比于hadoop體系,以數據庫的方式來做大數據處理更加簡單易用,學習成本低且靈活度高。當前社區(qū)仍舊在迅猛發(fā)展中,相信后續(xù)會有越來越多好用的功能出現。

摘自:https://zhuanlan.zhihu.com/p/98135840

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375