ClickHouse 與es比較

本文轉(zhuǎn)自https://zhuanlan.zhihu.com/p/368744398,僅做學(xué)習(xí)記錄使用,如有侵權(quán)請聯(lián)系刪除

簡介:本文的主旨在于通過徹底剖析ClickHouse和Elasticsearch的內(nèi)核架構(gòu),從原理上講明白兩者的優(yōu)劣之處,同時會附上一份覆蓋多場景的測試報告給讀者作為參考。

Clickhouse是俄羅斯搜索巨頭Yandex開發(fā)的完全列式存儲計(jì)算的分析型數(shù)據(jù)庫。ClickHouse在這兩年的OLAP領(lǐng)域中一直非常熱門,國內(nèi)互聯(lián)網(wǎng)大廠都有大規(guī)模使用。Elasticsearch是一個近實(shí)時的分布式搜索分析引擎,它的底層存儲完全構(gòu)建在Lucene之上。簡單來說是通過擴(kuò)展Lucene的單機(jī)搜索能力,使其具有分布式的搜索和分析能力。Elasticsearch通常會和其它兩個開源組件Logstash(日志采集)和Kibana(儀表盤)一起提供端到端的日志/搜索分析的功能,常常被簡稱為ELK。

今天很多用戶在實(shí)際的業(yè)務(wù)場景中,常常面對ClickHouse和Elasticsearch技術(shù)選型的難題。用戶對ClickHouse和Elasticsearch的內(nèi)核知識了解不足,往往只能通過性能測試的手段來進(jìn)行選型。本文的主旨在于通過徹底剖析ClickHouse和Elasticsearch的內(nèi)核架構(gòu),從原理上講明白兩者的優(yōu)劣之處,同時會附上一份覆蓋多場景的測試報告給讀者作為參考。

分布式架構(gòu)

Elasticsearch和ClickHouse都是支持分布式多機(jī)的數(shù)據(jù)產(chǎn)品,這里作者首先要比對的就是兩者的分布式架構(gòu)差異,分布式結(jié)構(gòu)設(shè)計(jì)對產(chǎn)品的易用性和可擴(kuò)展性具有非常重要的影響。在分布式架構(gòu)上,核心要解決的幾個問題包括:節(jié)點(diǎn)發(fā)現(xiàn)、Meta同步、副本數(shù)據(jù)同步。Elasticsearch作為一個老牌的開源產(chǎn)品,在這塊上做的相對比較成熟。原生的節(jié)點(diǎn)發(fā)現(xiàn)、Meta同步協(xié)議,給用戶非常好的易用性體驗(yàn)。Elasticsearch的Meta同步協(xié)議需要解決的問題其實(shí)和開源的Raft協(xié)議非常相似,只不過在Elasticsearch誕生的時候還沒有Raft出現(xiàn),所以就只能自己動手搞一個了。經(jīng)過這么多年的打磨,Elasticsearch的Meta同步協(xié)議也是相當(dāng)成熟了。依托于此,Elasticsearch具有非常易用的多角色劃分,auto schema inference等功能。值得一提的是Elasticsearch的多副本數(shù)據(jù)同步,并沒有復(fù)用Meta同步協(xié)議,而是采用傳統(tǒng)的主備同步機(jī)制,由主節(jié)點(diǎn)負(fù)責(zé)同步到備節(jié)點(diǎn),這種方式會更加簡單高效。

ClickHouse的分布式架構(gòu)能力相對會簡單一些,這也是因?yàn)镃lickHouse還是一個比較年輕的開源產(chǎn)品,還處在分布式易用性不斷迭代上升的階段。ClickHouse引入了外置的ZooKeeper集群,來進(jìn)行分布式DDL任務(wù)(節(jié)點(diǎn)Meta變更)、主備同步任務(wù)等操作的下發(fā)。多副本之間的數(shù)據(jù)同步(data shipping)任務(wù)下發(fā)也是依賴于ZooKeeper集群,但最終多副本之間的數(shù)據(jù)傳輸還是通過Http協(xié)議來進(jìn)行點(diǎn)對點(diǎn)的數(shù)據(jù)拷貝,同時多副本都可寫,數(shù)據(jù)同步是完全多向的。至于節(jié)點(diǎn)發(fā)現(xiàn),ClickHouse目前都沒有這方面的能力,都是需要通過手動配置集群節(jié)點(diǎn)地址來解決。ClickHouse目前這種腳手架式的分布式架構(gòu),導(dǎo)致它具有極強(qiáng)的靈活部署能力和運(yùn)維介入能力,對用戶的易用性略差,用戶門檻相對較高,但是在能力上限方面,ClickHouse的分布式部署擴(kuò)展性并沒有短板,集群規(guī)模上限對比Elasticsearch沒有差異。ClickHouse架構(gòu)扁平,沒有前端節(jié)點(diǎn)和后端節(jié)點(diǎn)之分,可部署任意規(guī)模集群。同時ClickHouse在多副本功能上有更細(xì)粒度的控制能力,可以做到表級別副本數(shù)配置,同一物理集群可劃分多個邏輯集群,每個邏輯機(jī)器可任意配置分片數(shù)和副本數(shù)。

存儲架構(gòu)

寫入鏈路設(shè)計(jì)

寫入吞吐能力是大數(shù)據(jù)場景下的一項(xiàng)核心指標(biāo),用戶對大數(shù)據(jù)產(chǎn)品的要求不光是要存的下,還要寫得快。這里首先介紹Elasticsearch的實(shí)時寫入鏈路設(shè)計(jì):在Elasticsearch的每一個Shard中,寫入流程分為兩部分,先寫入Lucene,再寫入TransLog。寫入請求到達(dá)Shard后,先寫Lucene內(nèi)存索引,此時數(shù)據(jù)還在內(nèi)存里面,接著去寫TransLog,寫完TransLog后,刷新TransLog數(shù)據(jù)到磁盤上,寫磁盤成功后,請求返回給用戶。這里有幾個關(guān)鍵點(diǎn),一是把寫Lucene放在了最前面,主要是防止用戶的寫入請求包含“非法”的數(shù)據(jù)。二是寫Lucene索引后,并不是可被搜索的,需要通過refresh把內(nèi)存的對象轉(zhuǎn)成完整的Segment后,然后再次reopen后才能被搜索,這個refresh時間間隔是用戶可設(shè)定的??梢钥闯鯨ucene索引并沒有寫入實(shí)時可見的能力,所以Elasticsearch是一個近實(shí)時(Near Real Time)的系統(tǒng)。最后是每隔一段比較長的時間,比如30分鐘后,Lucene會把內(nèi)存中生成的新Segment刷新到磁盤上,刷新后索引文件已經(jīng)持久化了,歷史的TransLog就沒用了,才會清空掉舊的TransLog。

1.png

△Elasticsearch單Shard寫入鏈路

2.png

△ClickHouse單Shard寫入鏈路

對比Elasticsearch的寫入鏈路,ClickHouse的寫入方式更加“簡單直接”、極致,上面已經(jīng)講過Elasticsearch是一個近實(shí)時系統(tǒng),內(nèi)存存儲引擎中新寫入的數(shù)據(jù)需要定時flush才可見。而ClickHouse則是干脆徹底放棄了內(nèi)存存儲引擎這一功能,所有的數(shù)據(jù)寫入時直接落盤,同時也就省略了傳統(tǒng)的寫redo日志階段。在極高寫入吞吐要求的場景下,Elasticsearch和ClickHouse都需要為了提升吞吐而放棄部分寫入實(shí)時可見性。只不過ClickHouse主推的做法是把數(shù)據(jù)延遲攢批寫入交給客戶端來實(shí)現(xiàn)。另外在多副本同步上,Elasticsearch要求的是實(shí)時同步,也就是寫入請求必須寫穿多個副本才會返回,而ClickHouse是依賴于ZooKeeper做異步的磁盤文件同步(data shipping)。在實(shí)戰(zhàn)中ClickHouse的寫入吞吐能力可以遠(yuǎn)遠(yuǎn)超過同規(guī)格的Elasticsearch。

Segment vs DataPart

Elasticsearch和ClickHouse的存儲設(shè)計(jì)外表上看起來非常相似,但能力卻又截然不同。Elasticsearch的磁盤文件由一個個Segment組成,Segment實(shí)際上是一份最小單位的Lucene索引,關(guān)于Segment內(nèi)部的存儲格式這里不展開討論。而Segment又會在后臺異步合并,這里合并主要解決兩個問題:1)讓二級索引更加有序;2)完成主鍵數(shù)據(jù)變更。二級索引是一種“全局”有序的索引,全部數(shù)據(jù)構(gòu)建到一個索引里面比構(gòu)建到多個索引里對查詢的加速更明顯。Elasticsearch是支持主鍵刪除更新的,這都是依托于Lucene索引的刪除功能來實(shí)現(xiàn)的,更新操作會被轉(zhuǎn)換成刪除操作加寫入操作。當(dāng)Lucene索引的Segment里存在多條刪除記錄時,系統(tǒng)就需要通過Segment合并來剔除這些記錄。在多個Segment進(jìn)行合并的時候,Lucene索引中的存儲數(shù)據(jù)表現(xiàn)出的是append-only的合并,這種方式下二級索引的合并就不需要進(jìn)行“重排序”。

對比Elasticsearch中的Segment,ClickHouse存儲中的最小單位是DataPart,一次批量寫入的數(shù)據(jù)會落盤成一個DataPart。DataPart內(nèi)部的數(shù)據(jù)存儲是完全有序的狀態(tài)(按照表定義的order by排序),這種有序存儲就是一種默認(rèn)聚簇索引可以用來加速數(shù)據(jù)掃描。ClickHouse也會對DataPart進(jìn)行異步合并,其合并也是用來解決兩個問題:1)讓數(shù)據(jù)存儲更加有序;2)完成主鍵數(shù)據(jù)變更。DataPart在合并存儲數(shù)據(jù)時表現(xiàn)出的是merge-sorted的方式,合并后產(chǎn)生的DataPart仍然處于完全有序狀態(tài)。依賴于DataPart存儲完全有序的設(shè)定,ClickHouse實(shí)現(xiàn)主鍵數(shù)據(jù)更新的方式和Elasticsearch截然不同。Elasticsearch在變更主鍵時,采用的是“先查原紀(jì)錄-生成新記錄-刪除原紀(jì)錄-寫入新紀(jì)錄”的方式,這種方式完全限制住了主鍵更新的效率,主鍵更新寫入和append-only寫入的效率差異非常大。而ClickHouse的主鍵更新是完全異步進(jìn)行的,主鍵相同的多條記錄在異步合并的時候會產(chǎn)生最新的記錄結(jié)果。這種異步批量的主鍵更新方式比Elasticsearch更加高效。

最后總結(jié)一下Segment和DataPart內(nèi)部文件存儲的能力差異,Segment完全就是Lucene索引的存儲格式,Lucene索引在倒排文件上的存儲毋庸置疑是做到極致的,Lucene索引同時也提供了行存、列存等不同格式的原數(shù)據(jù)存儲。Elasticsearch默認(rèn)都會把原數(shù)據(jù)存兩份,一份在行存里,一份在列存里。Elasticsearch會根據(jù)查詢的pattern,選擇掃描的合適的存儲文件。原生ClickHouse的DataPart中并沒有任何二級索引文件,數(shù)據(jù)完全按列存儲,ClickHouse實(shí)現(xiàn)的列存在壓縮率、掃描吞吐上都做到了極致。相對而言Elasticsearch中的存儲比較中庸,并且成本至少翻倍。

再談Schemaless

講到Elasticsearch的特性,大家都會提到Schemaless這個詞,Elasticsearch可以自動推斷寫入數(shù)據(jù)的json-shema,根據(jù)寫入數(shù)據(jù)的json-schema調(diào)整存儲表的Meta結(jié)構(gòu),這可以幫助用戶節(jié)省很多建表、加列的麻煩。但是在作者看來,Elasticsearch的這種能力其實(shí)叫auto schema inference更為恰當(dāng),這都得益于Elasticsearch的分布式Meta同步能力。而Elasticsearch的存儲其實(shí)是需要schema的,甚至是強(qiáng)綁定schema的,因?yàn)樗且远壦饕秊楹诵牡拇鎯?,沒有類型的字段又如何能構(gòu)建索引呢?真正的Schemaless應(yīng)該是可以靈活高效變更字段類型,同時保證查詢性能不會大幅下降的能力。今天用戶想變更Elasticsearch index中的某個字段類型,那只有一種方法:就是把整份數(shù)據(jù)數(shù)據(jù)reindex。相對比,ClickHouse的存儲反而不是強(qiáng)綁定schema的,因?yàn)镃lickHouse的分析能力是以存儲掃描為核心的,它是可以在數(shù)據(jù)掃描進(jìn)行動態(tài)類型轉(zhuǎn)換,也可以在DataPart合并的時候慢慢異步調(diào)整字段的類型,在查詢的時候字段類型變更引起的代價也就是運(yùn)行時增加cast算子的開銷,用戶不會感受到急劇的性能下降。作者認(rèn)為Schemeless絕對不是Elasticsearch的護(hù)城河能力,相對反而是它的弱項(xiàng)。至于auto schema inference,這是對小規(guī)模用戶非常友好的能力,但它永遠(yuǎn)不可能能幫用戶創(chuàng)建出性能最佳的Schema,在大數(shù)據(jù)量場景下大家還是需要根據(jù)具體的查詢需求來創(chuàng)建Schema,所有的便利最后都是有成本代價的。

查詢架構(gòu)

計(jì)算引擎

作者在這里把ClickHouse和Elasticsearch擺在一起講計(jì)算引擎其實(shí)有些荒謬的味道,因?yàn)镋lasticsearch實(shí)現(xiàn)的只是一個通用化搜索引擎。而搜索引擎能處理的查詢復(fù)雜度是確定的、有上限的,所有的搜索查詢經(jīng)過確定的若干個階段就可以得出結(jié)果,但是計(jì)算引擎則不然。Elasticsearch雖然也有SQL支持的插件,但是這種插件的實(shí)現(xiàn)邏輯就是把簡單的SQL查詢翻譯到確定的搜索模式上面。對于搜索引擎原來就不支持的數(shù)據(jù)分析行為,Elasticsearch-SQL也無濟(jì)于事。另外Elasticsearch-SQL當(dāng)前的翻譯能力看起來并不是非常完備和智能,為了獲得最高的搜索性能用戶還是需要嘗試Elasticsearch原生的查詢API。對于習(xí)慣使用SQL的用戶而言,Elasticsearch的查詢API是完全陌生的一套體系,復(fù)雜查詢非常難寫。

Elasticsearch的搜索引擎支持三種不同模式的搜索方式:query_and_fetch,query_then_fetch,dfs_query_then_fetch。第一種模式很簡單,每個分布式節(jié)點(diǎn)獨(dú)立搜索然后把得到的結(jié)果返回給客戶端,第二種模式是每個分布式存儲節(jié)點(diǎn)先搜索到各自TopN的記錄Id和對應(yīng)的score,匯聚到查詢請求節(jié)點(diǎn)后做重排得到最終的TopN結(jié)果,最后再請求存儲節(jié)點(diǎn)去拉取明細(xì)數(shù)據(jù)。這里設(shè)計(jì)成兩輪請求的目的就是盡量減少拉取明細(xì)的數(shù)量,也就是磁盤掃描的次數(shù)。最后一種方式是為了均衡各個存儲節(jié)點(diǎn)打分的標(biāo)準(zhǔn),先統(tǒng)計(jì)全局的TF(Term Frequency)和DF(Document Frequency),再進(jìn)行query_then_fetch。Elasticsearch的搜索引擎完全不具備數(shù)據(jù)庫計(jì)算引擎的流式處理能力,它是完全回合制的request-response數(shù)據(jù)處理。當(dāng)用戶需要返回的數(shù)據(jù)量很大時,就很容易出現(xiàn)查詢失敗,或者觸發(fā)GC。一般來說Elasticsearch的搜索引擎能力上限就是兩階段的查詢,像多表關(guān)聯(lián)這種查詢是完全超出其能力上限的。

ClickHouse的計(jì)算引擎特點(diǎn)則是極致的向量化,完全用c++模板手寫的向量化函數(shù)和aggregator算子使得它在聚合查詢上的處理性能達(dá)到了極致。配合上存儲極致的并行掃描能力,輕松就可以把機(jī)器資源跑滿。ClickHouse的計(jì)算引擎能力在分析查詢支持上可以完全覆蓋住Elasticsearch的搜索引擎,有完備SQL能力的計(jì)算引擎可以讓用戶在處理數(shù)據(jù)分析時更加靈活、自由。

數(shù)據(jù)掃描

ClickHouse是完全列式的存儲計(jì)算引擎,而且是以有序存儲為核心,在查詢掃描數(shù)據(jù)的過程中,首先會根據(jù)存儲的有序性、列存塊統(tǒng)計(jì)信息、分區(qū)鍵等信息推斷出需要掃描的列存塊,然后進(jìn)行并行的數(shù)據(jù)掃描,像表達(dá)式計(jì)算、聚合算子都是在正規(guī)的計(jì)算引擎中處理。從計(jì)算引擎到數(shù)據(jù)掃描,數(shù)據(jù)流轉(zhuǎn)都是以列存塊為單位,高度向量化的。而Elasticsearch的數(shù)據(jù)掃描如上一節(jié)所述,主要發(fā)生在query和fetch階段。其中query階段主要是掃描Lucene的索引文件獲取查詢命中的DocId,也包括掃描列存文件進(jìn)行聚合計(jì)算。而fetch階段主要是點(diǎn)查Lucene索引中的行存文件讀取明細(xì)結(jié)果。表達(dá)式計(jì)算和聚合計(jì)算在兩個階段都有可能發(fā)生,其計(jì)算邏輯都是以行為單位進(jìn)行運(yùn)算??偟膩碚fElasticsearch的數(shù)據(jù)掃描和計(jì)算都沒有向量化的能力,而且是以二級索引結(jié)果為基礎(chǔ),當(dāng)二級索引返回的命中行數(shù)特別大時(涉及大量數(shù)據(jù)的分析查詢),其搜索引擎就會暴露出數(shù)據(jù)處理能力不足的短板。

再談高并發(fā)

很多用戶談到ClickHouse,都會有一個錯誤的映像,ClickHouse查詢跑得快,但是并發(fā)不行。但這背后的原因其實(shí)是ClickHouse的并行太牛逼了,這是ClickHouse的一大強(qiáng)項(xiàng),一個查詢就可以把磁盤吞吐都打滿,查詢并行完全不依賴于shard,可以任意調(diào)整。不可否認(rèn)處理并發(fā)請求的吞吐能力是衡量一個數(shù)據(jù)系統(tǒng)效率的最終指標(biāo),ClickHouse的架構(gòu)上并沒有什么天然的并發(fā)缺陷,只不過它是個耿直boy,查詢需要掃描的數(shù)據(jù)量和計(jì)算復(fù)雜度擺在那,ClickHouse只是每次都老老實(shí)實(shí)計(jì)算而已,機(jī)器的硬件能力就決定了它的并發(fā)上限。ClickHouse的并發(fā)能力事實(shí)上是不錯的,認(rèn)為它并發(fā)不行是個誤區(qū)。只是默認(rèn)情況下ClickHouse的目標(biāo)是保證單個query的latency足夠低;部分場景下用戶可以通過設(shè)置合適的系統(tǒng)參數(shù)來提升并發(fā)能力,比如max_threads等。反過來,在這里介紹一下為什么有些場景下Elasticsearch的并發(fā)能力會很好。首先從Cache設(shè)計(jì)層面來看,Elasticsearch的Cache包括Query Cache, Request Cache,Data Cache,Index Cache,從查詢結(jié)果到索引掃描結(jié)果層層的Cache加速,就是因?yàn)镋lasticsearch認(rèn)為它的場景下存在熱點(diǎn)數(shù)據(jù),可能被反復(fù)查詢。反觀ClickHouse,只有一個面向IO的UnCompressedBlockCache和系統(tǒng)的PageCache,為什么呢?因?yàn)镃lickHouse立足于分析查詢場景,分析場景下的數(shù)據(jù)和查詢都是多變的,查詢結(jié)果等Cache都不容易命中,所以ClickHouse的做法是始終圍繞磁盤數(shù)據(jù),具備良好的IO Cache能力。其次回到數(shù)據(jù)掃描粒度,Elasticsearch具備全列的二級索引能力,這些索引一般都是預(yù)熱好提前加載到內(nèi)存中的,即使在多變的查詢條件下索引查詢得到結(jié)果的代價也很低,拿到索引結(jié)果就可以按行讀取數(shù)據(jù)進(jìn)行計(jì)算。而原生ClickHouse并沒有二級索引的能力,在多變的查詢條件下只能大批量地去掃描數(shù)據(jù)過濾出結(jié)果(阿里云ClickHouse已經(jīng)具備二級索引能力,解決了這一問題,性能水平和Elasticsearch相當(dāng),后續(xù)性能測評部分會進(jìn)行詳細(xì)介紹)。但是Elasticsearch具備二級索引,并發(fā)能力就一定會好么?也不盡然,當(dāng)二級索引搜索得到的結(jié)果集很大時,查詢還是會伴隨大量的IO掃描,高并發(fā)就無從談起,除非Elasticsearch的Data Cache足夠大,把所有原數(shù)據(jù)都加載到內(nèi)存里來。

總結(jié)來說,Elasticsearch只有在完全搜索場景下面(where過濾后的記錄數(shù)較少),并且內(nèi)存足夠的運(yùn)行環(huán)境下,才能展現(xiàn)出并發(fā)上的優(yōu)勢。而在分析場景下(where過濾后的記錄數(shù)較多),ClickHouse憑借極致的列存和向量化計(jì)算會有更加出色的并發(fā)表現(xiàn)。兩者的側(cè)重不同而已,同時ClickHouse并發(fā)處理能力立足于磁盤吞吐,而Elasticsearch的并發(fā)處理能力立足于內(nèi)存Cache。ClickHouse更加適合低成本、大數(shù)據(jù)量的分析場景,它能夠充分利用磁盤的帶寬能力。

性能測試

在本章中,作者選取了用戶業(yè)務(wù)中多個具有代表性的數(shù)據(jù)場景,以此對Elasticsearch和ClickHouse做了一個全方面多角度的性能測試報告。具體的測試集群環(huán)境如下:

| Clickhouse | Elasticsearch | 節(jié)點(diǎn)數(shù) |
| CPU:8coreMemory:32GB存儲:ESSD PL1 1500GB | CPU:8coreMemory:32GB存儲:ESSD PL1 1500GB | 4 |

日志分析場景

作者在日志分析場景中選取了兩個具有代表性的查詢場景進(jìn)行對比測試,結(jié)果如下所示。從結(jié)果分析來看ClickHouse和Elasicsearch在兩個場景中的性能差距隨著where條件過濾后的記錄數(shù)增大而擴(kuò)大,在數(shù)據(jù)量更大的trace_log場景中,兩者的分析查詢性能差距一目了然。Elasticsearch和ClickHouse完整版建表語句和查詢下載:日志分析場景

access_log(數(shù)據(jù)量197921836)

ClickHouse中的建表語句如下:

CREATE TABLE access_log_local on cluster default
(
  `sql` String, 
  `schema` String, 
  `type` String, 
  `access_ip` String, 
  `conn_id` UInt32, 
  `process_id` String, 
  `logic_ins_id` UInt32, 
  `accept_time` UInt64, 
  `_date` DateTime, 
  `total_time` UInt32, 
  `succeed` String, 
  `inst_name` String
) 
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(_date)
ORDER BY (logic_ins_id, accept_time);
CREATE TABLE access_log on cluster default as access_log_local
engine = Distributed(default, default, access_log_local, rand());

ClickHouse中的查詢語句如下:

--Q1
select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql,schema,succeed,process_id,inst_name from access_log where _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and logic_ins_id = 502680264 and accept_time <= 1609087111000 and accept_time >= 1609000711000 and positionCaseInsensitive(sql, 'select') > 0 order by accept_time desc limit 50,50;
--Q2
select 
case 
when total_time <=100 then 1 
when total_time > 100 and total_time <= 500 then 2 
when total_time > 500 and total_time <= 1000 then 3 
when total_time > 1000 and total_time <= 3000 then 4 
when total_time > 3000 and total_time <= 10000 then 5 
when total_time > 10000 and total_time <= 30000 then 6 
else 7 
end as reorder, 
case 
when total_time <=100 then '0~100ms' 
when total_time > 100 and total_time <= 500 then '100ms~500ms' 
when total_time > 500 and total_time <= 1000 then '500ms~1s' 
when total_time > 1000 and total_time <= 3000 then '1s~3s' 
when total_time > 3000 and total_time <= 10000 then '3s~10s' 
when total_time > 10000 and total_time <= 30000 then '10s~30s' 
else '30s以上' 
end as label, 
case 
when total_time <= 100 then '0~100' 
when total_time > 100 and total_time <= 500 then '100~500' 
when total_time > 500 and total_time <= 1000 then '500~1000' 
when total_time > 1000 and total_time <= 3000 then '1000~3000' 
when total_time > 3000 and total_time <= 10000 then '3000~10000' 
when total_time > 10000 and total_time <= 30000 then '10000~30000' 
else '30000~10000000000' 
end as vlabel, 
count() as value
from access_log
where logic_ins_id = 502867976 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000 
group by label,vlabel,reorder 
order by reorder;
--Q3
select toStartOfMinute(_date) as time, count() as value 
from access_log 
where logic_ins_id = 500152868 and accept_time <= 1609087111000 and accept_time >= 1609000711000  
group by time 
order by time;
--Q4
select count(*) as c from (
  select _date, accept_time, access_ip, type, total_time, concat(toString(total_time),'ms') as total_time_ms, sql, schema, succeed, process_id, inst_name 
  from access_log 
  where logic_ins_id = 501422856 and _date >= '2020-12-27 00:38:31' and _date <= '2020-12-28 00:38:31' and accept_time <= 1609087111000 and accept_time >= 1609000711000
);

性能對比如下:

[圖片上傳失敗...(image-2634a-1621237573532)]

trace_log(數(shù)據(jù)量569816761)

ClickHouse中的建表語句如下:

CREATE TABLE trace_local on cluster default
(
  `serviceName` LowCardinality(String), 
  `host` LowCardinality(String), 
  `ip` String, 
  `spanName` String, 
  `spanId` String, 
  `pid` LowCardinality(String), 
  `parentSpanId` String, 
  `ppid` String, 
  `duration` Int64, 
  `rpcType` Int32, 
  `startTime` Int64, 
  `traceId` String, 
  `tags.k` Array(String), 
  `tags.v` Array(String), 
  `events` String,
  KEY trace_idx traceId TYPE range
) ENGINE = MergeTree() 
PARTITION BY intDiv(startTime, toInt64(7200000000)) 
PRIMARY KEY (serviceName, host, ip, pid, spanName) 
ORDER BY (serviceName, host, ip, pid, spanName, tags.k);
CREATE TABLE trace on cluster default as trace_local
engine = Distributed(default, default, trace_local, rand());

ClickHouse中的查詢語句如下:

--Q1
select *
from trace
prewhere
traceId ='ccc6084420b76183'
where startTime > 1597968000300000  and startTime <  1598054399099000 settings max_threads = 1;
--Q2
select count(*) count, spanName as name from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by spanName
order by count desc limit 1000;
--Q3
select host as name, count(*) count
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by host;
--Q4
select count(*) count, tags.k as name  from trace
array join tags.k
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by tags.k;
--Q5
select count(*) spancount, 
sum(duration) as sumDuration, intDiv(startTime, 1440000000) as timeSel
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000
group by timeSel;
--Q6
select count(*) spanCount, 
countIf(duration  <=1000000), countIf(duration > 1000000),  countIf(duration > 3000000)
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000;
--Q7
select  host, startTime,traceId,spanName,duration,tags.k,tags.v
from trace
where serviceName ='conan-dean-user-period'
and startTime > 1597968000300000  and startTime <  1598054399099000 limit 1000000;

性能對比如下:

官方Ontime測試集

Ontime測試集是ClickHouse官網(wǎng)上推薦的一個分析型查詢benchmark,為了更加公證公開地對比ClickHouse和Elasticsearch在分析查詢上的性能差異。作者也引入了這個數(shù)據(jù)集進(jìn)行測試比對,結(jié)果如下所示,ClickHouse在純分析型查詢場景下具有巨大性能優(yōu)勢。Elasticsearch和ClickHouse完整版建表語句和查詢下載:聚合分析場景

用戶畫像場景(數(shù)據(jù)量262933269)

用戶畫像場景也是用戶比較難選擇使用Elasticsearch還是ClickHouse的一個典型場景,該場景的具體特點(diǎn)是超大寬表,大批量更新寫入,查詢返回的數(shù)據(jù)量大,篩選條件復(fù)雜多變。用戶在使用Elasticsearch時遇到的難點(diǎn)問題主要有兩個:數(shù)據(jù)寫不進(jìn)去,導(dǎo)入慢;數(shù)據(jù)拉不出來,返回大規(guī)模明細(xì)數(shù)據(jù)非常慢。針對這個場景,作者根據(jù)真實(shí)用戶的業(yè)務(wù)場景,mock了一張接近150列的大寬表進(jìn)行相關(guān)的查詢測試,具體的查詢?nèi)缦滤?,每條查詢返回的結(jié)果集在10萬到100萬行級別。Elasticsearch和ClickHouse完整版建表語句和查詢下載:用戶畫像場景

ClickHouse中的查詢語句如下:

--Q1
select user_id
from person_tag
where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1;
--Q2
select user_id
from person_tag
where mock_7_day_receive_cnt > 8 and like_fitness = 1 and mock14d_share_cnt > 8 settings append_squashing_after_filter = 1;
--Q3
select user_id
from person_tag
where home_perfer_mock_score > 8 and mock7d_access_homepage_cnt > 8 settings append_squashing_after_filter = 1;
--Q4
select user_id
from person_tag
where is_send_register_coupon > 8 and mock1d_like > 8 settings append_squashing_after_filter = 1;
--Q5
select user_id
from person_tag
where like_sports = 1 and like_3c = 1 and sex = 1 and like_dance = 1 and mock1d_share_cnt > 6 settings append_squashing_after_filter = 1;
--Q6
select user_id
from person_tag
where mock14d_access_homepage_cnt > 8 and like_anime = 1 settings append_squashing_after_filter = 1;
--Q7
select user_id,offline_ver,is_visitor,mock1d_comment_like,reg_days,mock14d_share_cnt,mock_30_order_avg_delivery_time_cnt,mock7d_comment_cnt,performance_rate,mock3d_valid_user_follow_cnt,mock30d_consume_content_cnt,like_cnt,like_photo,ls90_day_access_days,mock3d_release_trend_cnt,mock14d_access_homepage_range,qutdoor_perfer_mock_score,mock3d_access_homepage_cnt,mock_15_order_avg_delivery_time_cnt,mock7d_release_trend_cnt,like_food,mock30d_follow_topic_cnt,mock7d_is_access_topic,like_music,mock3d_interactive_cnt,mock14d_valid_user_follow_cnt,reg_platform,mock_7_day_lottery_participate_cnt,pre_churn_users,etl_time,like_anime,mock14d_access_homepage_cnt,mock14d_consume_content_cnt,like_travel,like_watches,mock14d_comment_like,ls30_day_access_days,mock14d_release_trend_cnt,ftooeawr_perfer_mock_score,mock7d_valid_user_follow_cnt,beauty_perfer_mock_score
from person_tag
where mock3d_like > 8 and mock3d_consume_content_cnt > 8 and mock_10_day_product_avg_amt < 1 settings append_squashing_after_filter = 1;

查詢性能結(jié)果對比如下,可以看出Elasticsearch在掃描導(dǎo)出大量結(jié)果數(shù)據(jù)的場景下,性能非常大,返回的結(jié)果集越大越慢,其中Q5是查詢命中結(jié)果集很小的對比case。

二級索引點(diǎn)查場景(數(shù)據(jù)量1000000000)

在分析查詢業(yè)務(wù)場景中,用戶難免會有幾個明細(xì)點(diǎn)查case,例如根據(jù)日志traceId查詢明細(xì)信息。開源ClickHouse因?yàn)闆]有二級索引能力,在遇到這種情況時,查詢性能對比Elasticsearch完全落后。阿里云ClickHouse自研了二級索引能力,補(bǔ)齊了這方面的短板,作者在這里專門加了一個二級索引點(diǎn)查的場景來進(jìn)行性能對比測試。Elasticsearch和ClickHouse完整版建表語句和查詢下載:二級索引點(diǎn)查場景

ClickHouse中的建表語句如下:

CREATE TABLE point_search_test_local on cluster default (
 `PRI_KEY` String, 
 `SED_KEY` String,  
 `INT_0` UInt32, 
 `INT_1` UInt32, 
 `INT_2` UInt32, 
 `INT_3` UInt32, 
 `INT_4` UInt32, 
 `LONG_0` UInt64, 
 `LONG_1` UInt64, 
 `LONG_2` UInt64, 
 `LONG_3` UInt64, 
 `LONG_4` UInt64, 
 `STR_0` String, 
 `STR_1` String, 
 `STR_2` String, 
 `STR_3` String, 
 `STR_4` String, 
 `FIXSTR_0` FixedString(16), 
 `FIXSTR_1` FixedString(16), 
 `FIXSTR_2` FixedString(16), 
 `FIXSTR_3` FixedString(16), 
 `FIXSTR_4` FixedString(16), 
 KEY SED_KEY_IDX SED_KEY Type range
) ENGINE = MergeTree ORDER BY PRI_KEY 
SETTINGS index_granularity_bytes = 4096, secondary_key_segment_min_rows = 1000000000, min_rows_for_wide_part = 2000000000;
CREATE TABLE point_search_test on cluster default as point_search_test_local
engine = Distributed(default, default, point_search_test_local, rand());

ClickHouse中的查詢模板語句如下:

select * from point_search_test where SED_KEY = 'XXX' settings max_threads = 1;

最終的查詢性能對比如下,阿里云ClickHouse具備二級索引能力后,其點(diǎn)查能力完全不弱于Elasticsearch,存儲原生支持的二級索引能力,具有極致性能。(阿里云ClickHouse二級索引文檔

數(shù)據(jù)導(dǎo)入性能對比

前面列舉的所有數(shù)據(jù)集數(shù)據(jù),作者都使用了ESSD本地文件導(dǎo)入的方式測試對比了Elasticsearch和ClickHouse的導(dǎo)入性能。ClickHouse可以直接使用ClickHouse-Client讀取各種格式的本地文件進(jìn)行導(dǎo)入,而Elasticsearch則是通過配置Logstash任務(wù)。具體耗時結(jié)果如下:

結(jié)語

Elasticsearch最擅長的主要是完全搜索場景(where過濾后的記錄數(shù)較少),在內(nèi)存富裕運(yùn)行環(huán)境下可以展現(xiàn)出非常出色的并發(fā)查詢能力。但是在大規(guī)模數(shù)據(jù)的分析場景下(where過濾后的記錄數(shù)較多),ClickHouse憑借極致的列存和向量化計(jì)算會有更加出色的并發(fā)表現(xiàn),并且查詢支持完備度也更好。ClickHouse的并發(fā)處理能力立足于磁盤吞吐,而Elasticsearch的并發(fā)處理能力立足于內(nèi)存Cache,這使得兩者的成本區(qū)間有很大差異,ClickHouse更加適合低成本、大數(shù)據(jù)量的分析場景,它能夠充分利用磁盤的帶寬能力。數(shù)據(jù)導(dǎo)入和存儲成本上,ClickHouse更加具有絕對的優(yōu)勢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容