
- 前言 -
Hadoop生態(tài)圈的技術(shù)繁多。HDFS一直用來保存底層數(shù)據(jù),地位牢固。Hbase作為一款Nosql也是Hadoop生態(tài)圈的核心組件,它海量的存儲(chǔ)能力,優(yōu)秀的隨機(jī)讀寫能力,能夠處理一些HDFS不足的地方。Clickhouse是一個(gè)用于聯(lián)機(jī)分析(OLAP)的列式數(shù)據(jù)庫管理系統(tǒng)(DBMS)。能夠使用SQL查詢實(shí)時(shí)生成分析數(shù)據(jù)報(bào)告。它同樣擁有優(yōu)秀的數(shù)據(jù)存儲(chǔ)能力。Apache Kudu是Cloudera Manager公司16年發(fā)布的新型分布式存儲(chǔ)系統(tǒng),結(jié)合CDH和Impala使用可以同時(shí)解決隨機(jī)讀寫和sql化數(shù)據(jù)分析的問題。分別彌補(bǔ)HDFS靜態(tài)存儲(chǔ)和Hbase Nosql的不足。既然可選的技術(shù)路線有這么多,本文將從安裝部署、架構(gòu)組成、基本操作等方面橫向?qū)Ρ纫幌翲base、Kudu和Clickhouse。另外這里還引入了幾個(gè)大廠的實(shí)踐作為例子予以參考。
- 安裝部署方式對(duì)比 -
具體的安裝步驟不過多贅述,這里只簡要比較安裝過程中需要依賴的外部組件。

- Habse 安裝 -
依賴HDFS作為底層存儲(chǔ)插件 依賴Zookeeper作為元數(shù)據(jù)存儲(chǔ)插件。
- Kudu 安裝 -
依賴Impala作為輔助分析插件 依賴CDH集群作為管理插件,但是不是必選的,也可以單獨(dú)安裝。
- ClickHouse 安裝 -
依賴Zookeeper作為元數(shù)據(jù)存儲(chǔ)插件和Log Service以及表的 catalog service組成架構(gòu)對(duì)比。




綜上所示,Hbase和Kudu都是類似于Master-slave的架構(gòu)而Clickhouse不存在Master結(jié)構(gòu),Clickhouse的每臺(tái)Server的地位都是等價(jià)的,是multi-master模式。不過Hbase和Clickhouse額外增加了一個(gè)Zookeeper作為輔助的元數(shù)據(jù)存儲(chǔ)或者是log server等,而Kudu的元數(shù)據(jù)是Master管理的,為了避免server頻繁從Master讀取元數(shù)據(jù),server會(huì)從Master獲取一份元數(shù)據(jù)到本地,但是會(huì)有元數(shù)據(jù)丟失的風(fēng)險(xiǎn)。
- 基本操作對(duì)比 -
數(shù)據(jù)讀寫操作



Clickhouse是個(gè)分析型數(shù)據(jù)庫。這種場景下,數(shù)據(jù)一般是不變的,因此Clickhouse對(duì)update、delete的支持是比較弱的,實(shí)際上并不支持標(biāo)準(zhǔn)的update、delete操作。Clickhouse通過alter方式實(shí)現(xiàn)更新、刪除,它把update、delete操作叫做mutation(突變)。標(biāo)準(zhǔn)SQL的更新、刪除操作是同步的,即客戶端要等服務(wù)端反回執(zhí)行結(jié)果(通常是int值);而Clickhouse的update、delete是通過異步方式實(shí)現(xiàn)的,當(dāng)執(zhí)行update語句時(shí),服務(wù)端立即反回,但是實(shí)際上此時(shí)數(shù)據(jù)還沒變,而是排隊(duì)等著。首先,使用where條件找到需要修改的分區(qū);然后,重建每個(gè)分區(qū),用新的分區(qū)替換舊的,分區(qū)一旦被替換,就不可回退;對(duì)于每個(gè)分區(qū),可以認(rèn)為是原子性的;但對(duì)于整個(gè)mutation,如果涉及多個(gè)分區(qū),則不是原子性的。?更新功能不支持更新有關(guān)主鍵或分區(qū)鍵的列;?更新操作沒有原子性,即在更新過程中select結(jié)果很可能是一部分變了,一部分沒變,從上邊的具體過程就可以知道;?更新一旦提交,不能撤銷,即使重啟Clickhouse服務(wù),也會(huì)繼續(xù)按照system.mutations的順序繼續(xù)執(zhí)行;?已完成更新的條目不會(huì)立即刪除,保留條目的數(shù)量由finished_mutations_to_keep存儲(chǔ)引擎參數(shù)確定。超過數(shù)據(jù)量時(shí)舊的條目會(huì)被刪除;?更新可能會(huì)卡住,比如update intvalue='abc’這種類型錯(cuò)誤的更新語句執(zhí)行不過去,那么會(huì)一直卡在這里,此時(shí),可以使用KILL MUTATION來取消。綜上所示,Hbase隨機(jī)讀寫,但是Hbase的update操作不是真的update,它的實(shí)際操作是insert一條新的數(shù)據(jù),打上不同的timestamp,而老的數(shù)據(jù)會(huì)在有效期之后自動(dòng)刪除。而Clickhouse干脆就不支持update和delete。
- 數(shù)據(jù)查詢操作 -
不支持標(biāo)準(zhǔn)sql,需要集成Phoenix插件。Hbase自身有Scan操作,但是不建議執(zhí)行,一般會(huì)全量掃描導(dǎo)致集群崩潰。與Impala集成實(shí)現(xiàn)查詢。HBASE在滴滴出行的應(yīng)用場景和最佳實(shí)踐
HBase在滴滴主要存放了以下四種數(shù)據(jù)類型:?統(tǒng)計(jì)結(jié)果、報(bào)表類數(shù)據(jù):主要是運(yùn)營、運(yùn)力情況、收入等結(jié)果,通常需要配合Phoenix進(jìn)行SQL查詢。數(shù)據(jù)量較小,對(duì)查詢的靈活性要求高,延遲要求一般。?原始事實(shí)類數(shù)據(jù):如訂單、司機(jī)乘客的GPS軌跡、日志等,主要用作在線和離線的數(shù)據(jù)供給。數(shù)據(jù)量大,對(duì)一致性和可用性要求高,延遲敏感,實(shí)時(shí)寫入,單點(diǎn)或批量查詢。?中間結(jié)果數(shù)據(jù):指模型訓(xùn)練所需要的數(shù)據(jù)等。數(shù)據(jù)量大,可用性和一致性要求一般,對(duì)批量查詢時(shí)的吞吐量要求高。?線上系統(tǒng)的備份數(shù)據(jù):用戶把原始數(shù)據(jù)存在了其他關(guān)系數(shù)據(jù)庫或文件服務(wù),把HBase作為一個(gè)異地容災(zāi)的方案。
- 訂單事件 -
近期訂單的查詢會(huì)落在Redis,超過一定時(shí)間范圍,或者當(dāng)Redis不可用時(shí),查詢會(huì)落在HBase上。業(yè)務(wù)方的需求如下:?在線查詢訂單生命周期的各個(gè)狀態(tài),包括status、event_type、order_detail等信息。主要的查詢來自于客服系統(tǒng);?在線歷史訂單詳情查詢。上層會(huì)有Redis來存儲(chǔ)近期的訂單,當(dāng)Redis不可用或者查詢范圍超出Redis,查詢會(huì)直接落到HBase;?離線對(duì)訂單的狀態(tài)進(jìn)行分析?寫入滿足每秒10K的事件,讀取滿足每秒1K的事件,數(shù)據(jù)要求在5s內(nèi)可用。
按照這些要求,我們對(duì)Rowkey做出了下面的設(shè)計(jì),都是很典型的scan場景。Rowkey:reverse(order_id) + (MAX_LONG - TS)Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS)Columns:用戶在時(shí)間范圍內(nèi)的訂單及其他信息。
- 司機(jī)乘客軌跡 -
舉幾個(gè)使用場景上的例子:用戶查看歷史訂單時(shí),地圖上顯示所經(jīng)過的路線;發(fā)生司乘糾紛,客服調(diào)用訂單軌跡復(fù)現(xiàn)場景;地圖部門用戶分析道路擁堵情況。
?滿足App用戶或者后端分析人員的實(shí)時(shí)或準(zhǔn)實(shí)時(shí)軌跡坐標(biāo)查詢;?滿足給出一個(gè)指定的地理范圍,取出范圍內(nèi)所有用戶的軌跡或范圍內(nèi)出現(xiàn)過的用戶。其中,關(guān)于第三個(gè)需求,地理位置查詢,我們知道MongoDB對(duì)于這種地理索引有源生的支持,但是在滴滴這種量級(jí)的情況下可能會(huì)發(fā)生存儲(chǔ)瓶頸,HBase存儲(chǔ)和擴(kuò)展性上沒有壓力但是沒有內(nèi)置類似MongoDB地理位置索引的功能,沒有就需要我們自己實(shí)現(xiàn)。通過調(diào)研,了解到關(guān)于地理索引有一套比較通用的GeohHash算法 。把GeoHash和其他一些需要被索引的維度拼裝成Rowkey,真實(shí)的GPS點(diǎn)為Value,在這個(gè)基礎(chǔ)上封裝成客戶端,并且在客戶端內(nèi)部對(duì)查詢邏輯和查詢策略做出速度上的大幅優(yōu)化,這樣就把HBase變成了一個(gè)MongoDB一樣支持地理位置索引的數(shù)據(jù)庫。如果查詢范圍非常大(比如進(jìn)行省級(jí)別的分析),還額外提供了MR的獲取數(shù)據(jù)的入口。兩種查詢場景的Rowkey設(shè)計(jì)如下:?單個(gè)用戶按訂單或時(shí)間段查詢:reverse(user_id) + (Integer.MAX_LONG-TS/1000);?給定范圍內(nèi)的軌跡查詢:reverse(geohash) + ts/1000 + user_id。

- ETA -
ETA是指每次選好起始和目的地后,提示出的預(yù)估時(shí)間和價(jià)格。提示的預(yù)估到達(dá)時(shí)間和價(jià)格,最初版本是離線方式運(yùn)行,后來改版通過HBase實(shí)現(xiàn)實(shí)時(shí)效果,把HBase當(dāng)成一個(gè)KeyValue緩存,帶來了減少訓(xùn)練時(shí)間、可多城市并行、減少人工干預(yù)的好處。整個(gè)ETA的過程如下:1.模型訓(xùn)練通過Spark Job,每30分鐘對(duì)各個(gè)城市訓(xùn)練一次;2.模型訓(xùn)練第一階段,在5分鐘內(nèi),按照設(shè)定條件從HBase讀取所有城市數(shù)據(jù);3.模型訓(xùn)練第二階段在25分鐘內(nèi)完成ETA的計(jì)算;4.HBase中的數(shù)據(jù)每隔一段時(shí)間會(huì)持久化至HDFS中,供新模型測試和新的特征提取。Rowkey:salting+cited+type0+type1+type2+TS Column:order, feature

- 監(jiān)控工具 DCM -
用于監(jiān)控Hadoop集群的資源使用(Namenode,Yarn container使用等),關(guān)系數(shù)據(jù)庫在時(shí)間維度過程以后會(huì)產(chǎn)生各種性能問題,同時(shí)我們又希望可以通過SQL做一些分析查詢,所以使用Phoenix,使用采集程序定時(shí)錄入數(shù)據(jù),生產(chǎn)成報(bào)表,存入HBase,可以在秒級(jí)別返回查詢結(jié)果,最后在前端做展示:

- 小結(jié) -
在滴滴推廣和實(shí)踐HBase的工作中,我們認(rèn)為至關(guān)重要的兩點(diǎn)是幫助用戶做出良好的表結(jié)構(gòu)設(shè)計(jì)和資源的控制。有了這兩個(gè)前提之后,后續(xù)出現(xiàn)問題的概率會(huì)大大降低。良好的表結(jié)構(gòu)設(shè)計(jì)需要用戶對(duì)HBase的實(shí)現(xiàn)有一個(gè)清晰的認(rèn)識(shí),大多數(shù)業(yè)務(wù)用戶把更多精力放在了業(yè)務(wù)邏輯上,對(duì)架構(gòu)實(shí)現(xiàn)知之甚少,這就需要平臺(tái)管理者去不斷幫助和引導(dǎo),有了好的開端和成功案例后,通過這些用戶再去向其他的業(yè)務(wù)方推廣。
資源隔離控制則幫助我們有效減少集群的數(shù)量,降低運(yùn)維成本,讓平臺(tái)管理者從多集群無止盡的管理工作中解放出來,將更多精力投入到組件社區(qū)跟進(jìn)和平臺(tái)管理系統(tǒng)的研發(fā)工作中,使業(yè)務(wù)和平臺(tái)都進(jìn)入一個(gè)良性循環(huán),提升用戶的使用體驗(yàn),更好地支持公司業(yè)務(wù)的發(fā)展。網(wǎng)易考拉基于KUDU構(gòu)建實(shí)時(shí)流量數(shù)倉實(shí)踐
Kudu不但提供了行級(jí)的插入、更新、刪除API,同時(shí)也提供了接近Parquet性能的批量掃描操作。使用同一份存儲(chǔ),既可以進(jìn)行隨機(jī)讀寫,也可以滿足數(shù)據(jù)分析的要求。實(shí)時(shí)流/業(yè)務(wù)數(shù)據(jù)寫入
可以使用Spark Streaming 提供的KafkaUtils.createDirectStream方法來創(chuàng)建一個(gè)對(duì)應(yīng)topic的Dstream。這種方法topic的offset將完全由我們自己控制:private val stream = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams)
)
?按照解析邏輯解析流量日志并構(gòu)建DataFrameval offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
val spark = SparkSession.builder.config(rdd.sparkContext.getConf).getOrCreate()
val kuduContext = new KuduContext(kuduMaster, spark.sparkContext)
val flowDf = spark.createDataFrame(rdd.map(r => {
processFlowLine(r.value)
}).filter(row => if (row.get(0) == null) false else true), schema)
kuduContext.upsertRows(flowDf, "impala::kaola_kudu_internal.dwd_kl_flw_app_rt")
stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)

- 寫入性能測試 -
Kudu寫入表: haitao_dev_log.dwd_kl_flw_app_rt,分片: 240個(gè),Spark Streaming調(diào)度間隔: 15s。

可以發(fā)現(xiàn)75%的任務(wù)都在1s完成,有少數(shù)任務(wù)跑的較慢但整體在2s都跑完了,這里需要注意一種極端情況,由于Spark的默認(rèn)配置并發(fā)數(shù)是1,如果有一個(gè)進(jìn)程遲遲沒有跑完(一般是數(shù)據(jù)分布不均)那么后面的任務(wù)將會(huì)排隊(duì),直到最初的任務(wù)跑完才會(huì)去調(diào)度下一個(gè)任務(wù)。這樣會(huì)造成資源浪費(fèi),多個(gè)空閑的executor會(huì)一直等待最后一個(gè)executor,流量日志不要求順序插入,因此我們可以加大任務(wù)的并發(fā)數(shù)。具體參數(shù)設(shè)置:spark.streaming.concurrentJobs = N

- 小結(jié) -
目前實(shí)時(shí)寫入Kudu的流量日志在每日數(shù)十億條,寫入量在TB級(jí),而且已有實(shí)時(shí)流量拆解等業(yè)務(wù)依賴Kudu的底層流量數(shù)據(jù),接下來將會(huì)有更多的業(yè)務(wù)線遷移至Kudu以滿足不同維度下的分析需求。攜程CLICKHOUSE日志分析實(shí)踐
結(jié)合攜程的日志分析場景,日志進(jìn)入ES前已經(jīng)格式化成JSON,同一類日志有統(tǒng)一的Schema,符合ClickHouse Table的模式;日志查詢的時(shí)候,一般按照某一維度統(tǒng)計(jì)數(shù)量、總量、均值等,符合ClickHouse面向列式存儲(chǔ)的使用場景。偶爾有少量的場景需要對(duì)字符串進(jìn)行模糊查詢,也是先經(jīng)過一些條件過濾掉大量數(shù)據(jù)后,再對(duì)少量數(shù)據(jù)進(jìn)行模糊匹配,ClickHouse也能很好的勝任。另外我們發(fā)現(xiàn)90%以上的日志沒有使用ES的全文索引特性,因此我們決定嘗試用ClickHouse來處理日志。消費(fèi)數(shù)據(jù)到CLICKHOUSE

我們使用gohangout消費(fèi)數(shù)據(jù)到ClickHouse,關(guān)于數(shù)據(jù)寫入的幾點(diǎn)建議:?采用輪詢的方式寫ClickHouse集群的所有服務(wù)器,保證數(shù)據(jù)基本均勻分布。?大批次低頻率的寫入,減少parts數(shù)量,減少服務(wù)器merge,避免Too many parts異常。通過兩個(gè)閾值控制數(shù)據(jù)的寫入量和頻次,超過10w記錄寫一次或者30s寫一次。?寫本地表,不要寫分布式表,因?yàn)榉植际奖斫邮盏綌?shù)據(jù)后會(huì)將數(shù)據(jù)拆分成多個(gè)parts,并轉(zhuǎn)發(fā)數(shù)據(jù)到其它服務(wù)器,會(huì)引起服務(wù)器間網(wǎng)絡(luò)流量增加、服務(wù)器merge的工作量增加,導(dǎo)致寫入速度變慢,并且增加了Too many parts的可能性。
?建表時(shí)考慮partition的設(shè)置,之前遇到過有人將partition設(shè)置為timestamp,導(dǎo)致插入數(shù)據(jù)一直報(bào)Too many parts的異常。我們一般按天分partition。?主鍵和索引的設(shè)置、數(shù)據(jù)的亂序等也會(huì)導(dǎo)致寫入變慢。
- 數(shù)據(jù)展示 -
我們調(diào)研了像Supperset、Metabase、Grafana等幾個(gè)工具,最終還是決定采用在Kibana3上開發(fā)支持ClickHouse實(shí)現(xiàn)圖表展示。主要原因是Kibana3這種強(qiáng)大的數(shù)據(jù)過濾功能,很多系統(tǒng)都不具備,另外也考慮到遷移到其他系統(tǒng)成本較高,用戶短期內(nèi)難以適應(yīng)。
- 查詢優(yōu)化 -
Kibana中的Table Panel用于顯示日志的明細(xì)數(shù)據(jù),一般查詢最近1小時(shí)所有字段的數(shù)據(jù),最終只展示前500條記錄。這種場景對(duì)于ClickHouse來說非常不友好。針對(duì)這個(gè)問題,我們將table Panel的查詢分兩次進(jìn)行:第一次查詢單位時(shí)間間隔的數(shù)據(jù)量,根據(jù)最終顯示的數(shù)據(jù)量計(jì)算出合理查詢的時(shí)間范圍;第二次根據(jù)修正后的時(shí)間范圍,結(jié)合Table Panel中配置的默認(rèn)顯示的Column查詢明細(xì)數(shù)據(jù)。經(jīng)過這些優(yōu)化,查詢的時(shí)間可以縮短到原來的1/60,查詢的列可以減少50%,最終查詢數(shù)據(jù)量減少到原來的1/120;ClickHouse提供了多種近似計(jì)算的方法,用于提供相對(duì)較高準(zhǔn)確性的同時(shí)減少計(jì)算量;使用MATERIALIZED VIEW或者M(jìn)ATERIALIZED COLUMN將計(jì)算量放在平常完成,也能有效降低查詢的數(shù)據(jù)量和計(jì)算量。
- ClickHouse 基本運(yùn)維 -
總體來說ClickHouse的運(yùn)維比ES簡單,主要包括以下幾個(gè)方面的工作:?過期日志的清理,我們通過一個(gè)定時(shí)任務(wù)每天刪除過期日志的partition;?ClickHouse的監(jiān)控,使用ClickHouse-exporter+VictoriaMetrics+Grafana的實(shí)現(xiàn);?數(shù)據(jù)遷移,通過ClickHouse分布式表的特性我們一般不搬遷歷史數(shù)據(jù),只要將新的數(shù)據(jù)接入新集群,然后通過分布式表跨集群查詢。隨著時(shí)間的推移,歷史數(shù)據(jù)會(huì)被清理下線,當(dāng)老集群數(shù)據(jù)全部下線后,新老集群的遷移就完成了。確實(shí)需要遷移數(shù)據(jù)時(shí),采用ClickHouse_copier或者復(fù)制數(shù)據(jù)的方式實(shí)現(xiàn)。慢查詢,通過kill query終止慢查詢的執(zhí)行,并通過前面提到的優(yōu)化方案進(jìn)行優(yōu)化
Too many parts異常:Too many parts異常是由于寫入的part過多part的merge速度跟不上產(chǎn)生的速度,導(dǎo)致part過多的原因主要包括幾個(gè)方面:
?小批量、高頻次寫ClickHouse?寫的是ClickHouse的分布式表?ClickHouse設(shè)置的merge線程數(shù)太少了? 無法啟動(dòng):之前遇到過ClickHouse無法啟動(dòng)的問題,主要包括兩個(gè)方面:- 文件系統(tǒng)損壞,通過修復(fù)文件系統(tǒng)可以解決
- 某一個(gè)表的數(shù)據(jù)異常導(dǎo)致ClickHouse加載失敗,可以刪除異常數(shù)據(jù)后啟動(dòng),也可以把異常的文件搬到detached目錄,等ClickHouse起來后再attach文件恢復(fù)數(shù)據(jù)
將日志從ES遷移到ClickHouse可以節(jié)省更多的服務(wù)器資源,總體運(yùn)維成本更低,而且提升了查詢速度,特別是當(dāng)用戶在緊急排障的時(shí)候,這種查詢速度的成倍提升,對(duì)用戶的使用體驗(yàn)有明顯的改善。但是ClickHouse畢竟不是ES,在很多業(yè)務(wù)場景中ES仍然不可替代;ClickHouse也不僅只能處理日志,進(jìn)一步深入研究ClickHouse,讓ClickHouse在更多領(lǐng)域發(fā)揮更大的價(jià)值,是我們一直努力的方向。
- 總結(jié) -
這里簡單總結(jié)一下三者的特點(diǎn)。首先說一下Hbase與Kudu,可以說是Kudu師承Hbase,架構(gòu)是類似的master-slave結(jié)構(gòu)。Hbase的物理模型是master和regionserver,regionserver存儲(chǔ)的是region,region里邊很有很多store,一個(gè)store對(duì)應(yīng)一個(gè)列簇,一個(gè)store中有一個(gè)memstore和多個(gè)storefile,store的底層是hfile,hfile是hadoop的二進(jìn)制文件,其中HFile和HLog是Hbase兩大文件存儲(chǔ)格式,HFile用于存儲(chǔ)數(shù)據(jù),HLog保證可以寫入到HFile中。Kudu的物理模型是master和tserver,其中table根據(jù)hash和range分區(qū),分為多個(gè)tablet存儲(chǔ)到tserver中,tablet分為leader和follower,leader負(fù)責(zé)寫請求,follower負(fù)責(zé)讀請求,總結(jié)來說,一個(gè)ts可以服務(wù)多個(gè)tablet,一個(gè)tablet可以被多個(gè)ts服務(wù)(基于tablet的分區(qū),最低為2個(gè)分區(qū))。而Clickhouse的特點(diǎn)在于它號(hào)稱最快的查詢性能,雖然也能存儲(chǔ)數(shù)據(jù),但并不是他的強(qiáng)項(xiàng),而且Clickhouse還不能update/delete數(shù)據(jù)。最后從下面幾個(gè)維度來對(duì)比一下Hbase、Kudu和Clickhouse。
所以Hbase更適合非結(jié)構(gòu)化的數(shù)據(jù)存儲(chǔ);在既要求隨機(jī)讀寫又要求實(shí)時(shí)更新的場景,Kudu+Impala可以很好的勝任,當(dāng)然再結(jié)合CDH就更好了,瓶頸并不在Kudu,而在Impala的Apache部署。如果只要求靜態(tài)數(shù)據(jù)的極速查詢能力,Clickhouse則更好。作者:super_chenzhou
來源:
https://blog.csdn.net/qq_37067752/article/details/107686978