內(nèi)容來源: 2018 年 5 月 5 日,小米HBase研發(fā)工程師吳國泉在“ACMUG & CRUG 2018 成都站”進行《大數(shù)據(jù)時代系統(tǒng)體系架構(gòu)和對比:存儲與計算》演講分享。
摘要?
大數(shù)據(jù)時代,各種分布式框架層出不窮,存儲方面有: HDFS, ES, HBase… 計算方面有:MR, Spark, Flink等等。如何根據(jù)業(yè)務(wù)選取合適的技術(shù)方案,相信一定是大家都比較關(guān)心的問題,這次的分享就簡單談一談我對現(xiàn)在比較主流的分布式框架的理解,希望能和大家一起學(xué)習(xí)進步。歡迎大家加入架構(gòu)華山論劍:836442475 本群提供免費的學(xué)習(xí)指導(dǎo) 架構(gòu)資料 以及免費的解答 不懂得問題都可以在本群提出來 之后還會有職業(yè)生涯規(guī)劃以及面試指導(dǎo)?進群修改群備注:開發(fā)年限-地區(qū)-經(jīng)驗 方便架構(gòu)師解答問題
MySQL、HBase、Elastcisearch的特點和區(qū)別?
MySQL、HBase、Elastcisearch是目前比較流行的存儲方式。Mysql廣泛應(yīng)用于OLTP業(yè)務(wù),支持事務(wù),提供二級索引。HBase面向海量數(shù)據(jù)存儲,有良好的寫性能,讀性能稍差,不支持事務(wù)和二級索引。ES適用于復(fù)雜查詢和全文檢索,不支持事務(wù)。接下來我們將通過存儲方式和讀寫方式這兩個方面來分析他們的特點。
存儲方式
常見的存儲方式有行存和列存兩種。行存的形式如上圖,一條一條記錄連續(xù)存放,這種方式比較適合于線上,比如一次性讀取檢索到的數(shù)據(jù)的全部信息。列存儲適合于一些數(shù)據(jù)分析的業(yè)務(wù),這種情況下不需要全部信息,只需特定字段下的相關(guān)數(shù)據(jù)。
與前兩種方式不同,ES存儲的是倒排索引,適用于全文檢索的業(yè)務(wù)。如圖所示原始文檔的內(nèi)容在存儲的時候首先會進行分詞,然后這些分詞會被組合成字典,每個字典后有對應(yīng)的鏈表,鏈表保存的就是該分詞所在的文檔ID。這樣就可以通過一些關(guān)鍵字快速的定位到文檔信息。
讀寫方式?
Mysql的讀寫方式是典型的1+4,其特點在于所有的讀寫都有可能是隨機IO。而HBase的每張表都是由很多Region組成,寫模式下數(shù)據(jù)首先會被寫入內(nèi)存,當(dāng)內(nèi)存到達(dá)某個閾值之后會進行刷盤生成一個小文件,任何的更新、插入、刪除操作都被當(dāng)做寫操作,都是順序?qū)憙?nèi)存然后刷到盤中。讀的時候是通過組件定位到指定Region,然后遍歷Region上的所有小文件。這相當(dāng)于犧牲了讀性能來提高寫性能。ES的寫入類似于HBase,同樣是先寫內(nèi)存然后刷盤,不過性能上不如HBase,因為ES在創(chuàng)建倒排索引的時候不僅要做分組,還有評分、壓縮之類的操作。雖然ES寫入性能較差,但正因為在寫入的時候做了這些復(fù)雜的計算,所以獲得了很強的檢索功能。
上圖對MySQL、HBase、ES之間的特點進行了詳細(xì)的總結(jié)。關(guān)于一致性的問題,這里需要提一下。ES寫入數(shù)據(jù)的時候會創(chuàng)建索引,這個操作會耗費一定的時間,因此ES中數(shù)據(jù)從寫入到可以檢索到默認(rèn)的時間間隔為1s。
計算?
解決了數(shù)據(jù)存儲問題之后,接下來就是發(fā)現(xiàn)數(shù)據(jù)價值,這就要利用到計算框架。對此我們主要探討兩方面,離線計算和實時計算。
離線計算
移動計算優(yōu)于移動數(shù)據(jù)是MapReduce的早期思想,因此當(dāng)Map任務(wù)在HDFS節(jié)點啟動的時候,數(shù)據(jù)不用遷移就可以直接在數(shù)據(jù)中跑計算任務(wù),當(dāng)然Reduce階段還是要做匯總。需要注意的是即使內(nèi)存足夠,Map階段的數(shù)據(jù)也還是會落盤。
對于上圖中的 ,相信大家一眼就能求出解。而計算機求解的時候首先會將該方程轉(zhuǎn)換成下面的形式,然后假設(shè)一個初始值代入方程右邊獲得新的x值,接著進行不斷的迭代,當(dāng)上一個x值和當(dāng)前值的差值小于規(guī)定閾值的時候迭代結(jié)束。在AI和數(shù)據(jù)分析領(lǐng)域其實都會涉及到這樣的解方程形式以及迭代計算。如果用MapReduce來實現(xiàn)的話,每一次迭代都會啟動一個MapReduce任務(wù),相對來說代價還是很大的。
針對以上兩點問題,Spark提供了一種新的RDD數(shù)據(jù)結(jié)構(gòu),如果數(shù)據(jù)可以存放在內(nèi)存中就不會進行落盤。另外它還提供了更好的API,不僅是任務(wù)調(diào)度,在程序編寫方面也更加友好。
實時計算?
在講實時計算之前需要明確一點,離線計算不等于批量計算,實時計算也不等于流式計算。離線和實時指的是數(shù)據(jù)處理的延時,批量和流式則是數(shù)據(jù)處理的方式。其實流式計算是可以完成批量計算的工作的,之所以還有批量計算框架,是因為流式計算的設(shè)計難度遠(yuǎn)高于批量計算。google的流式計算負(fù)責(zé)人有過這樣的觀點——一個設(shè)計良好的流式系統(tǒng)可以完全取代批量系統(tǒng)。
目前實時計算有這樣幾個難點,分別是吞吐、exactly once、數(shù)據(jù)亂序。下面會分別介紹Storm、Spark、Flink針對這三點提出的解決方案。
Storm
上圖是Storm統(tǒng)計詞群的過程,首先由spout從輸入源中讀取一條數(shù)據(jù),然后上游bolt接收數(shù)據(jù)進行分詞,接著下游bolt根據(jù)key值接收數(shù)據(jù)并將數(shù)據(jù)入庫,最終得到統(tǒng)計結(jié)果。
如果中間的分詞系統(tǒng)掛了,storm會提供一個acker任務(wù),每個bolt在計算完之后都會向acker發(fā)送一個ack信息用來聲明任務(wù)執(zhí)行成功,當(dāng)整個流程中所有的ack信息都發(fā)送給acker之后,acker就認(rèn)為這條信息處理成功并返回成功消息給輸入源。這種場景下ack信息會隨著數(shù)據(jù)量增長,因此特別影響storm的性能,這也是早期我們認(rèn)為流式計算的吞吐量不如批量計算的一個重要原因。
如果在處理的過程中某個計算節(jié)點掛了,而另外的節(jié)點卻入庫成功,這時acker會認(rèn)為該條記錄已處理失敗進而重發(fā),導(dǎo)致DB中的部分?jǐn)?shù)據(jù)會重復(fù)累加。
Spark streaming
Spark streaming針對以上兩個問題進行了優(yōu)化。首先是關(guān)于吞吐,不再是一條一條處理而是小批量的處理,默認(rèn)間隔為1秒,這1秒內(nèi)所接收到的數(shù)據(jù)會被生成為一個batch然后向下游發(fā)送,也就是通過擴大粒度來提高吞吐。
我們都知道離線計算本來就是精準(zhǔn)計算架構(gòu),Spark streaming內(nèi)部是利用spark的邏輯來保證exactly once。
Flink
Flink不再是一條一條數(shù)據(jù)做ack,而是在每段數(shù)據(jù)之間打上checkpoint,然后針對每段數(shù)據(jù)進行確認(rèn),如果任務(wù)掛掉就會在上一次成功的checkpoint點重新恢復(fù)數(shù)據(jù)。通過這種方式將流式計算和容災(zāi)較好的結(jié)合起來。
流式計算會有一個窗口的概念,比如上圖中就有3個5秒窗口,方框中的編號代表事件發(fā)生的時間??梢钥吹降?秒的時候有兩條訪問事件,由于網(wǎng)絡(luò)的延遲問題很有可能這3秒的數(shù)據(jù)會被分到第二個5秒窗口中,導(dǎo)致數(shù)據(jù)不正確。造成這樣結(jié)果的原因是早期的流式框架在處理數(shù)據(jù)的時候,將接收數(shù)據(jù)的時間認(rèn)為是數(shù)據(jù)產(chǎn)生的時間。這里延伸出里了兩個概念,event time——數(shù)據(jù)真正產(chǎn)生的時間,process time——系統(tǒng)處理該數(shù)據(jù)的時間。對此最直觀的解決方案就是讓數(shù)據(jù)攜帶自身產(chǎn)生時的時間戳,流式系統(tǒng)以該時間戳為基準(zhǔn)。