OLAP如何選型?ClickHouse為何如此之快?
本次分享的題目為ClickHouse在有贊的實(shí)踐,主要介紹:
OLAP在有贊的發(fā)展
ClickHouse在有贊的平臺(tái)化工具建設(shè)
ClickHouse在有贊的應(yīng)用
未來(lái)規(guī)劃和一些探索
嘉賓GitHub網(wǎng)址:https://github.com/kaka11chen
OLAP在有贊的發(fā)展
1.?有贊OLAP發(fā)展

有贊在2018年引入了Presto以解決離線數(shù)據(jù)的交互式分析問(wèn)題。Presto采用的是MPP架構(gòu),全內(nèi)存?+ pipeline的技術(shù)實(shí)現(xiàn),比較適合交互式分析。我們主要將Presto應(yīng)用在臨時(shí)查詢、BI報(bào)表、元數(shù)據(jù)的分析等場(chǎng)景。
在2019年上半年我們引入了Druid。在沒(méi)有Druid之前,面對(duì)實(shí)時(shí)的場(chǎng)景我們只能通過(guò)Flink或者Storm去預(yù)計(jì)算好結(jié)果,將結(jié)果直接存儲(chǔ)在 KV (如Redis、HBase)里面然后再進(jìn)行查詢,這種方式開(kāi)發(fā)起來(lái)非常地繁瑣,因此我們引入了Druid來(lái)完成數(shù)據(jù)的實(shí)時(shí)分析。Druid的原理是預(yù)計(jì)算最細(xì)粒度的聚合,比如說(shuō)我有三個(gè)維度A、B、C,Druid會(huì)預(yù)計(jì)算A、B、C三個(gè)維度再加上TimeStamp的粒度,然后整個(gè)預(yù)計(jì)算這些結(jié)果,查詢的時(shí)候再通過(guò)這份結(jié)果進(jìn)行查詢,這種方式可以減少維度爆炸的問(wèn)題,相對(duì)來(lái)說(shuō)是一種比較平衡的方式。應(yīng)用場(chǎng)景主要包括實(shí)時(shí)監(jiān)控、實(shí)時(shí)分析、實(shí)時(shí)數(shù)據(jù)產(chǎn)品等。
2019年的下半年我們也同時(shí)引入了Kylin,主要應(yīng)用在對(duì)于一些精確度要求比較高,性能要求也比較高的離線數(shù)據(jù)分析場(chǎng)景比如商家后臺(tái)的一些場(chǎng)景。Kylin的實(shí)現(xiàn)原理是完全預(yù)聚合和立方體,它會(huì)把各種組合都在HBase里面算出來(lái)。當(dāng)然現(xiàn)在Kylin4的版本也慢慢走向了不是所有的聚合結(jié)果都幫你算出來(lái),我們這邊也在引入Kylin4,并且提交了一些patch。
2020年我們引入了ClickHouse,主要用來(lái)進(jìn)行實(shí)時(shí)數(shù)據(jù)分析。ClickHouse的原理是明細(xì)動(dòng)態(tài)聚合查詢,因?yàn)橐郧癉ruid都是幫你預(yù)聚合好,我們沒(méi)辦法直接通過(guò)明細(xì)數(shù)據(jù)進(jìn)行查詢。此外ClickHouse還能通過(guò)物化視圖做到類(lèi)似Druid的預(yù)聚合的功能,雖然物化視圖相對(duì)來(lái)說(shuō)比較簡(jiǎn)單一點(diǎn),這個(gè)后面也會(huì)講一下。我們的應(yīng)用場(chǎng)景包括SCRM、DMP、CDP、直播分析還有一些日志指標(biāo)分析等。
2.?有贊OLAP發(fā)展和選型

這里主要對(duì)以上幾款產(chǎn)品在技術(shù)、延時(shí)、SQL支持程度、生產(chǎn)數(shù)據(jù)成本、支持Join、去重方式等方面的表現(xiàn)進(jìn)行了對(duì)比。
Presto:技術(shù)方面,Presto采用的MPP系統(tǒng)和SQL On Hadoop;Presto的延時(shí)是天/小時(shí)級(jí)別,雖然現(xiàn)在數(shù)據(jù)湖IceBerg、HuDi比較火,他們希望把它達(dá)到分鐘級(jí)別,但是就目前來(lái)看還沒(méi)到很成熟大規(guī)模使用的階段;Presto的查詢延遲一般,因?yàn)樗菑拿骷?xì)層開(kāi)始查詢,沒(méi)有任何預(yù)聚合;SQL支持程度還是比較完善的;因?yàn)闆](méi)有預(yù)聚合,數(shù)據(jù)生產(chǎn)成本也比較低;Join也支持的比較好;去重的話也支持普通精確去重。
Druid:Druid采用了一些如位圖索引、字符串編碼、預(yù)聚合等的技術(shù),剛才也講過(guò)它只預(yù)聚合最細(xì)的維度組合,這樣可以防止維度爆炸,但是會(huì)犧牲一點(diǎn)RT(響應(yīng)時(shí)間),因此做了一個(gè)權(quán)衡;支持實(shí)時(shí);查詢延遲相對(duì)Presto會(huì)低很多;SQL支持的相對(duì)完善,但是沒(méi)有Presto那么完善;Druid會(huì)做一部分的預(yù)聚合,自然需要一些成本;新版本開(kāi)始慢慢準(zhǔn)備支持Join了,但還不成熟,維度表的Lookup是一直支持的;去重方式采用的是HyperLogLog,快手我看到也有第三方的contributing去支持BitMap去重,但是這個(gè)我也沒(méi)有深入調(diào)研過(guò),這邊就不多說(shuō)了。
Kylin:Kylin采用的技術(shù)是完全預(yù)聚合的立方體,至少Kylin4之前的版本是這樣的;它是把結(jié)果存到HBase,微批量的延遲;查詢延遲也非常低,因?yàn)橹苯訌腍Base里面去做相應(yīng)的結(jié)果查詢,如果一些比較輕微的聚合可以通過(guò)HBase的Coprocessor去做一些比較輕微的一些聚合;SQL支持程度也比較完善,生產(chǎn)成本由于要做預(yù)聚合的立方體成本就比較高,生產(chǎn)成本一高的話靈活性就會(huì)變得很差,比如修改一些字段,重刷數(shù)據(jù)成本就會(huì)比較高;Join也是支持的;Kylin有一個(gè)比較好的一個(gè)事情是可以做到BitMap去重這個(gè)會(huì)是一個(gè)比較大的優(yōu)勢(shì)。
ClickHouse:ClickHouse采用的技術(shù)是明細(xì)動(dòng)態(tài)聚合查詢,當(dāng)然也類(lèi)似于Druid可以用一些物化視圖,做一些預(yù)聚合的表,再通過(guò)預(yù)聚合的表進(jìn)行查詢;支持實(shí)時(shí),明細(xì)查詢延時(shí)還是比較低的,對(duì)比Presto、Impala包括對(duì)比Apache Doris,ClickHouse單表的查詢性能確實(shí)很高;Join在一些情況下性能不佳,因?yàn)镴oin沒(méi)有Exchange或者Shuffle Join,物化視圖是預(yù)聚合的所以性能會(huì)更好一點(diǎn);SQL支持只能說(shuō)較完善,沒(méi)有Presto和Kylin那么完善;生產(chǎn)數(shù)據(jù)如果是物化視圖的話,會(huì)聚合一下,因此還是會(huì)有一些成本;Join剛才也講了只能有限支持,比如說(shuō)沒(méi)有shuffle join,當(dāng)數(shù)據(jù)量比較大的情況下,性能就不太佳甚至?xí)椴怀鰜?lái),語(yǔ)法解析也不是很好,有時(shí)候比如說(shuō)左邊是個(gè)Table右邊是個(gè)SubQuery這種,但是換一換可能性能就會(huì)更差;去重方式支持精確去重和HyperLogLog,但是沒(méi)有支持BitMap去重。
3. ClickHouse特性
ClickHouse主要具有以下特性:
靈活,支持明細(xì)數(shù)據(jù)SQL查詢,并用物化視圖加速。
多核(垂直擴(kuò)展),可以在一臺(tái)機(jī)器上使用多線程去進(jìn)行查詢;分布式處理,它有不同的分片,這樣的話可以進(jìn)行水平擴(kuò)展,MPP架構(gòu)。
支持實(shí)時(shí)批量數(shù)據(jù)攝入。
列式存儲(chǔ)、向量化引擎、代碼編譯生成。向量化引擎和代碼編譯生成基本是為了解決算子瓶頸,如果不通過(guò)這些技術(shù)的話一般是個(gè)火山模型,火山模型會(huì)有一些虛函數(shù)以及分支判斷之間的一些開(kāi)銷(xiāo)。通過(guò)這兩種方法,向量化可以去平攤開(kāi)銷(xiāo),代碼編譯可以把它轉(zhuǎn)成以數(shù)據(jù)為中心進(jìn)而消除開(kāi)銷(xiāo)。但是這兩種方法也不是萬(wàn)能的,比如說(shuō)當(dāng)Aggregation或者Join數(shù)據(jù)量比較大時(shí)候需要物化到內(nèi)存,物化到內(nèi)存的時(shí)候瓶頸也就產(chǎn)生了,因此也不會(huì)有非常大的性能爭(zhēng)議。
主鍵索引,ClickHouse會(huì)按照用戶設(shè)置的主鍵進(jìn)行排序,ClickHouse中MergeTree的文件就是按照這個(gè)逐漸進(jìn)行排序的,Bloom Filter、minmax等做了二級(jí)索引。
當(dāng)然,ClickHouse也不是萬(wàn)能的,它在以下方面并不擅長(zhǎng):
沒(méi)有高速,低延遲的更新和刪除方法。不擅長(zhǎng)單行數(shù)據(jù),行級(jí)別數(shù)據(jù)的更新刪除方法一般都是異步進(jìn)行。
稀疏索引使得點(diǎn)查性能不佳。點(diǎn)查沒(méi)辦法用ClickHouse,最好的是用KV類(lèi)型的Redis或者HBASE。
不支持事務(wù)。盡管事務(wù)現(xiàn)在對(duì)OLAP也會(huì)有一些用途,但是不是非常大的用途。
ClickHouse的應(yīng)用場(chǎng)景:
用戶行為分析,精細(xì)化運(yùn)營(yíng)分析:日活、留存率分析、路徑分析、有序漏斗轉(zhuǎn)化率分析、Session分析等。
實(shí)時(shí)日志分析,監(jiān)控分析,實(shí)時(shí)數(shù)倉(cāng)。
4.?ClickHouse原理簡(jiǎn)介
很多人接觸ClickHouse都會(huì)問(wèn)的一個(gè)問(wèn)題,ClickHouse為什么會(huì)快?

計(jì)算方面:ClickHouse采用的是自底而上的設(shè)計(jì),極度關(guān)注性能,有各種優(yōu)化。如Aggregator,一般Aggregator沒(méi)有這么夸張的優(yōu)化,更多的是對(duì)Int數(shù)據(jù)的Hash表的優(yōu)化,其他類(lèi)型可能就用一樣的Hash表。ClickHouse對(duì)不同的數(shù)據(jù)類(lèi)型有不同的HashMap。前面的一些情況也是它的原因,比如向量化引擎和代碼編譯生成,向量化確實(shí)能夠提高,代碼編譯生成一般情況下大部分的數(shù)據(jù)也能提高,比如在表達(dá)式生成這種情況也會(huì)用代碼編譯生成。
存儲(chǔ)方面:采用的是MergeTree,一般我們寫(xiě)ClickHouse都會(huì)按批次寫(xiě),就是一個(gè)批次Insert過(guò)去,然后會(huì)形成part(partition)文件,假如只有一個(gè)分區(qū),就形成一個(gè)part文件,part文件是按照主鍵進(jìn)行排序的其內(nèi)部有序,ClickHouse后天會(huì)默默地把這些文件進(jìn)行合并,有點(diǎn)像LSM-Tree。主鍵包括以下幾個(gè)部分,首先有一個(gè)primary.idx是它是主鍵索引,該索引是稀疏索引而非稠密索引,這樣的好處是可以把稀疏索引放到內(nèi)存中性能會(huì)更佳,而且OLAP不是OLTP,它更多的是聚合計(jì)算,所以瓶頸更多是在聚合計(jì)算的算子那里,但是比如很多小查詢那就不一定了。然后主鍵索引會(huì)去找到它相應(yīng)的.mkr文件,是跟主鍵索引是一一對(duì)應(yīng)的。mkr文件記載主鍵索引的比如說(shuō)行號(hào)。后面是數(shù)據(jù)文件bin,記載了這兩個(gè)文件之間的offset。整體流程是先通過(guò)主鍵,然后找到MKR,然后再找到bin的offset。中間還有些壓縮之類(lèi)的東西,會(huì)復(fù)雜一點(diǎn),這里就先簡(jiǎn)單講一下。
1. ClickHouse集群部署

我們這邊大數(shù)據(jù)平臺(tái)是一個(gè)中臺(tái)部門(mén),所以更希望是我們這邊把整個(gè)大數(shù)據(jù)組件給包裝起來(lái)統(tǒng)一管理,業(yè)務(wù)方只需要使用包裝后的系統(tǒng)。
上層是應(yīng)用場(chǎng)景,剛才有講SCRM、DMP、CDP、企業(yè)微信助手。
應(yīng)用場(chǎng)景這塊,我們?cè)谧钋岸藭?huì)有個(gè)Load Balance,目前我們用的是LVS,其實(shí)HTTP代理也行。但是因?yàn)槲覀兊慕y(tǒng)一全權(quán)代理會(huì)有一些限制,因此我們現(xiàn)在選用的是LVS會(huì)比較方便一點(diǎn)。
第二層Proxy部分,目前我們用的是Apisix代理網(wǎng)關(guān),該網(wǎng)關(guān)是基于Nginx的,Nginx現(xiàn)在有一個(gè)基于OpenResty的Apisix的代理網(wǎng)關(guān)可以用來(lái)做一些網(wǎng)關(guān)或者邊緣網(wǎng)關(guān)。我們用它來(lái)做一些熔斷、限流、安全、日志這些功能。用起來(lái)也比較方便,DMP這邊我們還實(shí)現(xiàn)了一個(gè)自定義插件,這個(gè)后續(xù)會(huì)講。
下面一層是分布式表,分布式表之后就是每一個(gè)分片,每一個(gè)分片都有兩臺(tái)副本。比如說(shuō)這邊三個(gè)分片的話就是3x2共6臺(tái)機(jī)器。Zookeeper的話我們是用SSD單獨(dú)部署的,因?yàn)镃lickHouse在副本復(fù)制的時(shí)候需要Zookeeper去協(xié)調(diào)。上圖畫(huà)的不是那么的準(zhǔn),在分布式表跟分片之間我們是部署在同一些機(jī)器上面的,這樣比較省成本。
2.?ClickHouse寫(xiě)入

ClickHouse寫(xiě)入部分,離線我們一般是通過(guò)Spark將Hive表導(dǎo)到ClickHouse里面,還有通過(guò)Flink將Kafka的數(shù)據(jù)進(jìn)行導(dǎo)入。需要注意的是必須批量寫(xiě)入,原因剛才也講過(guò),因?yàn)镃lickHouse每次寫(xiě)入會(huì)根據(jù)partition形成parts文件,如果一條數(shù)據(jù)寫(xiě)一個(gè)parts文件的話會(huì)合并不過(guò)來(lái),因?yàn)楹竺婧喜⒌脑捑蜁?huì)看到合并比插入都慢的那種錯(cuò)誤,官方推薦是用批量方式寫(xiě)。
后面就是寫(xiě)本地表,讀分布式表。ClickHouse也可以寫(xiě)分布式表,但是對(duì)Zookeeper的性能壓力會(huì)比較大,整個(gè)性能也會(huì)比較差。我們有兩種寫(xiě)入方式,一種是Random一種是Hash,Hash是為了后面DMP系統(tǒng)特殊設(shè)計(jì)的,這個(gè)后面會(huì)講。我們Spark用的WaterDrop,是比較出名的一個(gè)Spark的工具,我們進(jìn)行了自己修改一些代碼定制。第一步是通過(guò)cluster name獲得分片的信息,我們是直接通過(guò)apisix代理獲得,apisix會(huì)寫(xiě)一個(gè)插件獲得相應(yīng)掛在后端的某一個(gè)ClickHouse Cluster分片的路徑,獲得之后我們就用Hash或者隨機(jī)的方式插入數(shù)據(jù),F(xiàn)link也是類(lèi)似的。

上圖展示的是我們離線導(dǎo)入和實(shí)時(shí)導(dǎo)入部分的。
離線導(dǎo)入:我們的數(shù)據(jù)平臺(tái)里面有一類(lèi)任務(wù)需要離線導(dǎo)入,用戶可以直接選一張Hive表或者ClickHouse的目標(biāo)表去導(dǎo)入。
分片策略:如果是Hash的話會(huì)需要采用分片策略。
實(shí)時(shí)導(dǎo)入:我們寫(xiě)了Flink的ClickHouse Connector,基于JDBC根據(jù)剛才那些要素進(jìn)行修改。并且它不僅SQL可以用這個(gè)Connector,SDK也能用這個(gè)Connector。
3.?ClickHouse離線讀寫(xiě)分離
我們做好這套系統(tǒng)后發(fā)現(xiàn)有一個(gè)痛點(diǎn)就是業(yè)務(wù)場(chǎng)景很多都寫(xiě)入量巨大,寫(xiě)多讀少,離線會(huì)導(dǎo)入很多Hive表。很多公司其實(shí)做一些產(chǎn)品出來(lái),查詢QPS都沒(méi)有很高,因?yàn)楫a(chǎn)品剛剛上線需要尋找業(yè)務(wù)爆發(fā)點(diǎn),沒(méi)有辦法一下子讀很多,但是寫(xiě)入量會(huì)很大。這種情況的話,我們針對(duì)寫(xiě)入量只能擴(kuò)分片來(lái)提高寫(xiě)入吞吐,其實(shí)就是一種讀寫(xiě)不分離的方法,所以資源會(huì)比較浪費(fèi)。
去年的話我看了那個(gè)QQ音樂(lè)的,他們也有這個(gè)思路去做ClickHouse讀寫(xiě)分離,我們這邊也就是同樣去做了一下。一般來(lái)說(shuō)可行方案有兩種,一種就是用k8s ch臨時(shí)集群來(lái)寫(xiě)入??梢杂靡粋€(gè)k8s集群,每次插入的時(shí)候用一個(gè)臨時(shí)的k8s集群構(gòu)建起來(lái),然后數(shù)據(jù)直接寫(xiě)到臨時(shí)集群,等插完之后再把相應(yīng)的數(shù)據(jù)文件直接弄到線上集群。第二種方法是使用Spark構(gòu)建數(shù)據(jù)文件。這種方法是可行的,但是相對(duì)來(lái)說(shuō)復(fù)雜度會(huì)高一點(diǎn)。我們采用的是k8s方案,因?yàn)楸容^容易實(shí)現(xiàn)。

ClickHouse的基本工作流程如上圖所示:
我們先講一下架構(gòu),我這邊會(huì)設(shè)計(jì)一個(gè)Master節(jié)點(diǎn)和一個(gè)StandBy Master節(jié)點(diǎn)去管理這些組件,包括高可用、擴(kuò)縮容這些。DataImportJob啟動(dòng)起來(lái)之后通過(guò)WaterDrop直接導(dǎo)入到k8s臨時(shí)寫(xiě)入集群,再導(dǎo)入某張表某個(gè)分區(qū)的時(shí)候會(huì)去啟動(dòng)一個(gè)臨時(shí)的k8s集群。集群里面我們用的是官方的ch-operator,它除了本身的ClickHouse server服務(wù),我們還自己寫(xiě)了一個(gè)叫CHSegmentPusher,字面上理解就是當(dāng)數(shù)據(jù)導(dǎo)入臨時(shí)集群之后,要把數(shù)據(jù)push到中間存儲(chǔ)(HDFS)里面去。相應(yīng)的ClickHouse集群這邊我們也有相應(yīng)的puller部署在正式集群的每個(gè)節(jié)點(diǎn)上,puller去從HDFS上取這個(gè)數(shù)據(jù)。
右側(cè)是Apache Helix,apache Helix Framework是基于Zookeeper去幫你管理集群心跳、存活、高可用、擴(kuò)縮容等。包括這個(gè)分布式任務(wù)隊(duì)列,也是用zk實(shí)現(xiàn)的。其實(shí)這個(gè)架構(gòu)有點(diǎn)像Druid那個(gè)segment加載,有了解Druid的小伙伴就比較清楚。
還有一些細(xì)節(jié),比如說(shuō)Helix里面我們自己去實(shí)現(xiàn)一個(gè)balance來(lái)保證相同分片的replicas形成一個(gè)partition。因?yàn)橐欢ㄒ@么做。
1.?DMP人群畫(huà)像系統(tǒng)

第一個(gè)是DMP人群畫(huà)像系統(tǒng),概括來(lái)說(shuō)就是一個(gè)正交導(dǎo)入的方式。我們DMP人群畫(huà)像一般業(yè)務(wù)都要去進(jìn)行人群圈選,通過(guò)一些tag去圈選出一些人群來(lái)進(jìn)行營(yíng)銷(xiāo)活動(dòng)或者是預(yù)估人群的數(shù)目。如果把這些明細(xì)數(shù)據(jù)直接放到ClickHouse里面的話性能就會(huì)比較差,所以業(yè)界通常的做法是把它轉(zhuǎn)成位圖,這部分查詢的性能就會(huì)相對(duì)高一點(diǎn)。我們的標(biāo)簽用戶位圖表基本上就是這樣設(shè)計(jì)的,首先是tag_id、tag_value、uid_bitmap、uid_bitmap_offset,比如說(shuō)我性別是男,就有一個(gè)標(biāo)簽uid_bitmap。至于為什么會(huì)有offset,是因?yàn)镃lickHouse的bitmap只支持32位,如果ID大于32位怎么辦呢?我們采用offerset讓不同的bitmap來(lái)解決這樣的問(wèn)題。在插入數(shù)據(jù)的時(shí)候,無(wú)論是實(shí)時(shí)還是批量數(shù)據(jù)我們都會(huì)通過(guò)UserID進(jìn)行哈希,這樣的話某一個(gè)UID會(huì)在某一個(gè)分片上固定下來(lái)不會(huì)到別的分片,在性能方面這是一種最高效的做法。如果不采用這種方式,我們也試過(guò)用分布式表,結(jié)果可能三臺(tái)機(jī)器比單臺(tái)還慢,這肯定是接受不了的,因?yàn)闆](méi)有辦法橫向擴(kuò)展或者縱向擴(kuò)展來(lái)提高性能,因此我們采用的正交哈希寫(xiě)入。一旦正交了之后,只要在自己的機(jī)器上面去處理本地的SQL,處理完之后直接將結(jié)果進(jìn)行合并就行,甚至不用merge。比如說(shuō)是人群的ID,那每個(gè)符合tag的人群的ID直接返回這個(gè)分片,然后另外一個(gè)也返回,然后他們把人群id疊加就行了,人數(shù)只要求個(gè)和就行了不用merge。這件事情我們不想讓用戶自己去客戶端去實(shí)現(xiàn),所以我們有apisix寫(xiě)了一個(gè)Customer Plugins,Customer Plugins去做這個(gè)事情,當(dāng)你發(fā)了一個(gè)查詢請(qǐng)求過(guò)來(lái)之后,會(huì)通過(guò)你的查詢請(qǐng)求去不同的分片上面去并發(fā)進(jìn)行查詢,然后結(jié)果按照剛才說(shuō)的進(jìn)行合并,讓用戶感到透明。但這個(gè)只針對(duì)靜態(tài)標(biāo)簽,或者是可枚舉的一些靜態(tài)標(biāo)簽。用戶還有一些實(shí)時(shí)標(biāo)簽是不可枚舉的,這部分現(xiàn)在是用的用戶行為表,業(yè)務(wù)方直接用用戶行為表明細(xì)數(shù)據(jù)進(jìn)行查詢,查詢完之后的結(jié)果轉(zhuǎn)換之后再跟剛才的靜態(tài)預(yù)計(jì)算位圖進(jìn)行位圖方面的計(jì)算最后得到結(jié)果。這個(gè)確實(shí)是有隱患的,因?yàn)闆](méi)有預(yù)計(jì)算如果量很大的情況下可能會(huì)比較慢。
2.?SCRM商家會(huì)員管理系統(tǒng)

SCRM系統(tǒng)是一個(gè)比較靈活的商家分析系統(tǒng),它維度可變,狀態(tài)回溯;支持動(dòng)態(tài)圈選;支持跨店、跨天去重。目前Druid做不了這個(gè)事情,所以說(shuō)我們用的ClickHouse。
3.?天網(wǎng)日志監(jiān)控TopN系統(tǒng)

我們公司有一個(gè)日志分析軟件叫天網(wǎng),全公司的應(yīng)用層日志全部都存在這個(gè)系統(tǒng)里面,還有一些監(jiān)控的TopN,錯(cuò)誤日志TopN、商家限流TopN、服務(wù)吞吐量TopN、特性/功能TopN。一開(kāi)始這個(gè)技術(shù)是用Druid實(shí)現(xiàn)的,我們最近把它切換成了Flink + ClickHouse,并且使用物化視圖進(jìn)行預(yù)聚合,整體效果還是不錯(cuò)的。數(shù)據(jù)量也是比較大的,每秒100w行數(shù)據(jù)的Kafka寫(xiě)入QPS。寫(xiě)入QPS還是比較高的,換成ClickHouse這套方案可以比Druid節(jié)省60%的機(jī)器而且性能也大大提升。這個(gè)系統(tǒng)還有個(gè)特點(diǎn)就是查詢QPS沒(méi)有那么高,因?yàn)楫吘箖?nèi)部使用,不是ToC的。
04
最后我講一下ClickHouse在有贊的未來(lái)規(guī)劃和一些探索:
1.?ClickHouse在有贊的未來(lái)規(guī)劃
ClickHouse容器化部署:容器化部署更高擁有更好的彈性伸縮能力,也能和其他的服務(wù)進(jìn)行混合部署來(lái)節(jié)省成本。我們覺(jué)得容器化部署是可以做的,雖然我們現(xiàn)在容器化部署的讀寫(xiě)分離已經(jīng)接入了這塊的支持,但是我們覺(jué)得如果把線上集群也進(jìn)行k8s化會(huì)帶來(lái)更大的收益。
ClickHouse更多的推廣,更多業(yè)務(wù)的接入:因?yàn)镃lickHouse在2020年剛流行起來(lái),好多以前的系統(tǒng)還是通過(guò)Druid、Kylin等OLAP組件去滿足,所以未來(lái)可以去做更多的業(yè)務(wù)接入。
ClickHouse更好的平臺(tái)化以及故障防范:平臺(tái)化方面我們做還不是非常完善,特別是業(yè)務(wù)方易用性、多租戶隔離、限流、熔斷、監(jiān)控報(bào)警,業(yè)務(wù)治理等方面需要更多的投入。
一些業(yè)務(wù)以前是Druid單鏈路,我們準(zhǔn)備把它改成Druid + ClickHouse的雙鏈路或者將Druid的業(yè)務(wù)遷移到ClickHouse。
2. ClickHouse當(dāng)前痛點(diǎn)
我們?cè)谑褂肅lickHouse的過(guò)程中發(fā)現(xiàn)了ClickHouse還沒(méi)有到完美的程度。
ClickHouse不太像傳統(tǒng)意義上的分布式數(shù)據(jù)庫(kù),整體來(lái)說(shuō)比較“手動(dòng)檔”,很多地方都需要用戶自己去設(shè)計(jì)一個(gè)流程去完善,包括寫(xiě)入和物化視圖。
沒(méi)有自動(dòng)Rebalance的能力,導(dǎo)致擴(kuò)容縮容運(yùn)維特別復(fù)雜。這個(gè)痛點(diǎn)還蠻大的,會(huì)增加運(yùn)維的工作量。
Join不是采用Shuffle/Exchange Join,數(shù)據(jù)量大的時(shí)候性能差。并且ClickHouse的Join語(yǔ)法也不是支持的非常好。
行級(jí)別的Update/Delete性能差,官方也不推薦。這個(gè)其實(shí)從業(yè)務(wù)層面來(lái)說(shuō),他們真的很需要這個(gè),但是很多OLAP就是不支持,因?yàn)橹С诌@個(gè)功能非常影響性能。業(yè)界比較多的是采用impala + kudu方案。
據(jù)我了解很多大廠都針對(duì)這些痛點(diǎn)進(jìn)行了嘗試改進(jìn)。比如說(shuō)存算分離,Exchange Join實(shí)現(xiàn)等很多大廠都在做。
那么Apache Doris呢?
現(xiàn)在Doris很火很多大廠已經(jīng)開(kāi)始嘗試使用了,相比ClickHouse,Doris更像是一個(gè)分布式數(shù)據(jù)庫(kù),也解決了部分痛點(diǎn),比如自動(dòng)平衡、支持Shuffle Join等。而且是國(guó)人做的也是蠻驕傲的事情。但是就目前為止單表的性能包括成熟度、穩(wěn)定性還不如ClickHouse需要繼續(xù)發(fā)展。
還有一些是沒(méi)有開(kāi)源的如Hologres、TIFlash等,這些可以嘗試以下,但是未知程度會(huì)比較高,穩(wěn)定性成熟度這些也不知道,也不知道未來(lái)是否開(kāi)源。
3.?CliCkHouse + Doris POC實(shí)現(xiàn)
于是我們產(chǎn)生了一個(gè)想法:如果我們能夠把兩者結(jié)合,我們利用高性能的ClickHouse算子實(shí)現(xiàn)替換基于Impala的Apache Doris,在未來(lái)甚至能打造出更好的分布式OLAP數(shù)據(jù)庫(kù)。
當(dāng)然現(xiàn)在只是POC實(shí)現(xiàn)階段,POC實(shí)現(xiàn)主要是驗(yàn)證可行性的,因此實(shí)現(xiàn)起來(lái)以快為主,很多地方可能只是臨時(shí)Mock之類(lèi)的。
驗(yàn)證主要是分成以下幾個(gè)步驟:
① 嘗試將Doris be的代碼一起編譯到ClickHouse中運(yùn)行起來(lái)。(DONE)
② 嘗試將解析相關(guān)fragments,Doris比較像分布式數(shù)據(jù)庫(kù),所以前端會(huì)發(fā)一個(gè)分布式執(zhí)行計(jì)劃,然后再到后端去解析,以前的話是基于impala這套機(jī)制再加上自己改動(dòng)去做這件事,現(xiàn)在把它映射成ClickHouse的幾個(gè)簡(jiǎn)單算子(Storage掃描、聚合、表達(dá)式),目前能跑一個(gè)簡(jiǎn)單的單機(jī)SQL。(DONE)
③ 嘗試將Doris OLAP的存儲(chǔ)實(shí)現(xiàn)成ClickHouse的Storage(StorageDorisOLAP),并且能跑一個(gè)簡(jiǎn)單的單機(jī)SQL,如果能用Doris的相關(guān)存儲(chǔ),也能使用其自動(dòng)平衡能力。因?yàn)镃lickHouse存儲(chǔ)一般是Storage xxx,比如說(shuō)Storage MergeTree。我們實(shí)現(xiàn)了StorageDorisOLAP,最終是能夠用到Doris的存儲(chǔ),Doris的存儲(chǔ)跟ClickHouse的存儲(chǔ)有點(diǎn)像,但Doris有一個(gè)最大的好處是可以自動(dòng)進(jìn)行平衡,并且最小單位是Tablit比物理的partition更小,在這方面會(huì)更有優(yōu)勢(shì)。(DONE)
④ 嘗試實(shí)現(xiàn)Doris exchange node的算子,能跑通一個(gè)簡(jiǎn)單的分布式SQL。(DONE)
總體來(lái)說(shuō),我覺(jué)得應(yīng)該是可行的,但是可能比較復(fù)雜,如果順利的話,后期我們會(huì)投入更多精力在這上面。
下面是一些運(yùn)行時(shí)的截圖。


4.?大數(shù)據(jù)數(shù)據(jù)庫(kù)未來(lái)的趨勢(shì)
云原生:可任意擴(kuò)縮容,存儲(chǔ)計(jì)算分離,多租戶,Serverless,按需付費(fèi)(Snowflake、BigQuery、SaaS/Daas)。其實(shí)從國(guó)外Snowflake和BigQueue這兩款軟件基本上就概括了這些,特別是Snowflake。
多模數(shù)據(jù)庫(kù):融合OLAP、OLTP能力,一站式解決(TiDB、TiFlash、Hologres)。我們面對(duì)很多用戶需求的比如說(shuō)日志分析的時(shí)候,他們又要查明細(xì)又要去查一些指標(biāo)分析,我們現(xiàn)在沒(méi)有辦法用一個(gè)數(shù)據(jù)庫(kù),只能ClickHouse加上ES或者加上TiDB、MySQL這種去處理。
利用新硬件:現(xiàn)在也有一些GPU、FPGA等新硬件能夠加速整個(gè)數(shù)據(jù)庫(kù)的發(fā)展。
Q:一級(jí)索引和二級(jí)索引之間有沒(méi)有什么關(guān)系?
A:這個(gè)應(yīng)該沒(méi)有什么關(guān)系的,一級(jí)索引就是它的主鍵,主鍵的索引就叫一級(jí)索引,主鍵索引就是你拿這個(gè)東西在MergeTree里進(jìn)行排序的。二級(jí)索引的話這些Bloom Filter、minmax那些,會(huì)比如說(shuō)你跳過(guò)整個(gè)塊,比如說(shuō)minmax不符合范圍。
Q:Order by是不是完全可以取代主鍵?
A:Order by它是如果你不配主鍵它就是主鍵,但是好像也可以去配主鍵,但一般沒(méi)人搞這么復(fù)雜。
Q:ClickHouse讀寫(xiě)分離下面的隊(duì)列是做什么的?
A:因?yàn)檫@里工作流程列的比較詳細(xì),我就沒(méi)有更細(xì)致的講,其實(shí)比如說(shuō)Spark把數(shù)據(jù)寫(xiě)入到k8s臨時(shí)集群之后,那總要有個(gè)方法去通知我們這邊的CHSegementPusher,然后CHSegementPusher接到了這個(gè)任務(wù)通知才會(huì)去把數(shù)據(jù)發(fā)到HDFS上面某一個(gè)目錄。同樣puller也是這樣的,左邊pusher全部發(fā)完之后puller也要去同樣的我們主的DataImportJob也會(huì)去Distributed Task Queue發(fā)任務(wù),讓相關(guān)的puller去從HDFS上面取這個(gè)數(shù)據(jù)。它就是用來(lái)push、puller這些通信的,但是push、puller這些東西其實(shí)是通過(guò)Apache Helix去管理的,Apache Helix是一套基于Zookeeper的一些集群管理的一套系統(tǒng),可以把這些東西定義成results,比如說(shuō)master、puller、push,不同角色我可以定義成results。
Q:數(shù)據(jù)去重是怎么處理的?
A:數(shù)據(jù)去重是指哪方面需要去重,因?yàn)槿绻麛?shù)據(jù)導(dǎo)入是沒(méi)有辦法做到Exactly-Once。從本質(zhì)來(lái)講,雖然ClickHouse現(xiàn)在有個(gè)block防止比如說(shuō)網(wǎng)絡(luò)抖動(dòng)的時(shí)候,相同的block它可以不再導(dǎo)入,這種可以防止一下去重,但是理論上如果不是這種情況,它沒(méi)有辦法做到Exactly-Once。如果真的要去重,還有一種方法就是用ReplacingMergeTree可以異步地進(jìn)行去重,當(dāng)然也可以用一些方法比如每隔多少時(shí)間去做一下optimize強(qiáng)制聚合的這么一個(gè)事情,但是這個(gè)不能太頻繁,因?yàn)檫@個(gè)操作會(huì)非常重。
