5.大數據生態圈成員和原理

11有界數據與無界數據
知識猶如人體的血液一樣寶貴。
——高士其
上一章我們學習了人工智能下的大數據,這一章我們會從原理、架構角度深入學習大數據生態系統各個組件。

爲了給後面的小節做鋪墊,這一小節我們來學習一下有界數據和無界數據這兩個概念。

在前面章節我們講過,大數據是具有4V 特徵:volume(容量)、variety(種類)、velocity(速度)、value(價值)。但是不管是怎樣的數據,按照時間上分割的話只有兩類:有界數據、無界數據。

有界數據:數據的開始和結束都有明確的定義。

無界數據:數據的開始有定義,結束沒有定義。

有界數據場景和無界數據場景

關於有界數據,我們每個人日常生活中會有很多親身體驗。例如中國移動、中國聯通、中國電信每個月發送到手機上的賬單。

手機卡月賬單數據計算的是一個月的第一天到最後一天時間範圍內所有接打電話、收發短信、使用流量瀏覽網絡等所有行爲產生費用信息。類似的有界數據還有每天晚上微信運動推送到手機上的步數排行榜、餘額寶每天的收益信息等等。

在另外一些場景,數據具有很強的連續性。例如:小區裏面的監控錄像數據、股票交易數據、電商平臺後臺交易數據。這些數據在一段時間裏會一直連續的產生出來,就像水流一樣,無窮無盡。像這樣的數據就叫做無界數據。

多數時候有界數據和無界數據之前是可以相互轉化的。例如:無界的電商的交易數據對於雙十一這一天來說就是有界的。用戶使用手機的消費數據相對於一個月來說是有界,但是如果從整個使用週期來看又是無界數據。

有界數據和無界數據通過對“時間窗口”進行轉化。

無界數據取某個特定的“時間窗口”就變成了有界數據、有界數據去掉“時間窗口”就變成了無界數據。

批處理和流處理

批處理(Batch Processing):對一批數據進行處理。

流處理(Stream Processing):對數據流進行處理。

顯而易見,批處理對應有界數據,流處理對應無界數據。

流處理需要在更短的內處理完海量的數據,相對於批處理來說有着更高的實時性、更小的延遲性要求。

在大數據剛剛興起的時候MapReduce 實現了人類對於歷史的有界數據批處理能力,人們並沒有想過流處理無界數據。

隨着MapReduce 不斷優化,以及計算速度更快的Spark 計算引擎出現以後,大數據技術對於批處理的需求已經能夠很好的滿足了。

進而人們開始關注流處理方面的需求。從開始的Storm 到Spark Streamming ,再到Flink 的出現。流處理框架經歷了多個版本的進化以後,在數據處理的實時性、吞吐量等關鍵性能指標上已經發展得比較完善了。

2019 年5G 在中國正式投入商用,全世界各國都將先後進入5G 時代。5G 時代,更多的物聯網設備接入到網絡中來,智能家居飛入了越來越多的普通家庭。

這些物聯網設備、智能家居設備運轉過程中會產生海量的無界數據,流處理技術的需求會越來越迫切。

總結

大數據按照時間上劃分爲:有界數據、無界數據。有界數據的處理叫批處理,無界數據的處理叫流處理。流處理相對於批處理有着更高的實時性、更小的延遲性要求。

大數據處理技術從具備批處理能力開始,發展到流處理能力。隨着5G 技術的發展,流處理場景會越來越多,促使大數據流技術向着更高效標準邁進。

12大數據系統架構演進

學習這件事不在乎有沒有人教你,最重要的是在於你自己有沒有覺悟和恆心。
—— 法布爾

上一小節我們講到了無界數據和有界數據的概念。這一小節我們來一起學習一下大數據系統架構的演進。

在實際大數據處理場景中,有界數據、無界數據的需求都非常常見,我們在設計大數據系統架構的時候需要考慮兼容這兩種數據處理場景。大數據系統架構經歷了兩代經典架構:Lambda 架構、Kappa 架構。

Lambda架構

著名的大數據實時計算框架Strom 的作者Nathan Marz 提出了Lambda 架構。

對於大數據處理系統Nathan Marz 有着自己的看法。他認爲一個設計良好的大數據系統需要具備以下特性:

Robustandfault-tolerant(容錯性和魯棒性):對大規模分佈式系統來說,機器是不可靠的,可能會當機,但是系統需要是健壯、行爲正確的,即使是遇到機器錯誤。

除了機器錯誤,人更可能會犯錯誤。在軟件開發中難免會有一些Bug,系統必須對有Bug 的程序寫入的錯誤數據有足夠的適應能力,所以比機器容錯性更加重要的容錯性是人爲操作容錯性。

對於大規模的分佈式系統來說,人和機器的錯誤每天都可能會發生,如何應對人和機器的錯誤,讓系統能夠從錯誤中快速恢復尤其重要。

Lowlatency reads and updates(低延時):很多應用對於讀和寫操作的延時要求非常高,要求對更新和查詢的響應是低延時的。

Scalable(橫向擴容):當數據量/負載增大時,可擴展性的系統通過增加更多的機器資源來維持性能。也就是常說的系統需要線性可擴展,通常採用scale out(通過增加機器的個數)而不是scale up(通過增強機器的性能)。

General(通用性):系統需要能夠適應廣泛的應用,包括金融領域、社交網絡、電子商務數據分析等。

Extensible(可擴展):需要增加新功能、新特性時,可擴展的系統能以最小的開發代價來增加新功能。

Allows ad hoc queries(方便查詢):數據中蘊含有價值,需要能夠方便、快速地查詢出所需要的數據。

Minimal maintenance(易於維護):系統要想做到易於維護,其關鍵是控制其複雜性,越是複雜的系統越容易出錯、越難維護。

Debuggable(易調試):當出問題時,系統需要有足夠的信息來調試錯誤,找到問題的根源。其關鍵是能夠追根溯源到每個數據生成點。

儘管大數據系統需要具備非常多的特性,但是Nathan Marz 對於數據系統的總結非常簡單。

數據系統 = 數據 + 查詢

基於以上對大數據系統的理解,Nathan Marz 提出Lambda 架構的目標是設計出一個能滿足實時大數據系統關鍵特性的架構,包括有:高容錯、低延時和可擴展等。

Lambda 架構整合離線計算和實時計算,融合不可變性(Immunability),讀寫分離和複雜性隔離等一系列架構原則,可集成Hadoop,Kafka,Storm,Spark,Hbase等各類大數據組件。

http://lambda-architecture.net/

Lambda架構有三個核心模塊組成:批處理層(batch layer)、實時處理層(speed layer)、服務層(serving layer)

Lambda架構核心邏輯有以下五點:

(1)所有進入系統的數據,都會被分發到批處理層(batch layer)和快速處理層(speed layer)。

(2)批處理層(batch layer)有兩個作用:

​ 管理master的數據(raw數據):比如用HDFS來存儲。

​ 爲數據轉換爲批處理視圖做預處理。

(3)服務層(serving layer)用於加載和實現數據庫中的批處理視圖,以便用戶能查詢。

(4)快速處理層(speed layer)用於處理新數據和服務層更新造成的高延遲補償。

(5)任何Query的答案,都能通過合併批處理視圖和實時視圖的結果來獲得。

Lambda 架構是一種通用的架構思想,任何滿足以上Lambda 架構核心邏輯的架構都可以歸爲Lambda 架構。正因爲Lambda 架構具備足夠的靈活性,在互聯網大數據系統架構中被廣泛應用。

以下是小米公司的OlAP 大數據系統架構,採用了Lambda 架構。

數據同時被灌入批處理層、快速處理層。批處理層使用Kylin 對歷史數據構建多維數據立方體。

快速處理層使用ES 和Kudu 計算實時數據。批處理層、快速處理層的計算結果在服務層合併在一起。

當OLAP 查詢通過服務層獲取數據結果時,服務層會根據查詢時間分別去批處理、快速處理層的結果集裏面查詢,並將兩個數據集進行合併,最終返回OLAP 查詢結果。

Lambda 架構通過批處理層、速度處理層的分離實現了拓展性、高容錯性,是一種極具價值的大數據架構方案。但是Lambda 架構的缺點也是顯而易見的。

爲了實現最終Query 查詢的無感知,批處理層、速度處理層需要實現兩套一樣的數據處理邏輯。處理邏輯稍有不同時就會產生錯誤的數據結果。當數據處理邏輯變複雜的時候,這種邏輯一致性維護壓力就非常明顯。

同時由於大數據系統本身運維就比較複雜,批處理層、速度處理層兩種不同的大數據框架運維起來壓力非常大。

Kappa架構

由於Lambda 架構具有顯而易見的缺點,Linkedin的Jaykreps 提出了Kappa 架構。

Kappa 架構的核心思想包括以下三點:

用Kafka或者類似的分佈式隊列系統保存數據,你需要幾天的數據量就保存幾天。
當需要全量重新計算時,重新起一個流計算實例,從頭開始讀取數據進行處理,並輸出到一個新的結果存儲中。
當新的實例做完後,停止老的流計算實例,並把老的一些結果刪除。
在Kappa 架構下,只有在有必要的時候纔會對歷史數據進行重複計算,實時計算和流式計算都是使用同一段代碼邏輯,同一計算框架。Kappa 架構中新的計算實例會輸出到新表中,即使新的計算邏輯有問題時舊計算實例輸出的舊錶依然存在,有着較高的容錯性。

Lambda架構 VS Kappa架構

Lambda 架構相對於kappa 架構出現的更早,Lambda 架構出現的時期,還沒有哪一種大數據計算引擎能同時很好地解決數據批處理和流式處理的問題。

Lambda 架構通過建立批處理層、速度層、服務層的方式,將批處理計算框架、實時計算框架計算的數據結果進行合併,既滿足了對歷史數據分析的需求,又滿足了數據實時性的要求。Lambda 構架是在當時基於有限條件下的一種臨時方案,具有歷史侷限性。

Lambda 架構在代碼維護複雜性、計算框架運維複雜性上的問題,Kappa 架構都針對性的進行了改善。然而Kappa 架構也並不是完美的。

因爲Kappa 架構對歷史數據的計算都是現計算的,當需要追溯的時間跨度很長、計算邏輯複雜的情況下,現計算需要的時間是否能被接受是一個問題。

總結
Lambda 架構是一種早起的大數據系統架構方案,它的出現讓人們具備了對歷史批量數據、流式數據同時處理的能力。Kappa 架構基於更強計算框架,可以使用同一套代碼邏輯、同一套計算框架,對歷史批量數據、流式數據統一處理,有着更低的代碼維護成本、計算集羣運維成本。

13 HDFS:先驅還是先烈

一個不注意小事情的人,永遠不會成功大事業。
——戴爾·卡耐基

這一小節我們的話題是HDFS 。帶着問題學習的方式往往會比較高效。同學們,我們在學習本小節過程思考一下這個問題:HDFS 是先驅還是先烈?

2004年 Hadoop第一個版本誕生之初就包含了HDFS 和MapReduce 。在此後的15 年裏基於Hadoop 的計算框架層出不窮,然而Spark、Flink 這些計算框架想要在Hadoop 生態圈站住腳就必須支持HDFS 作爲文件存儲系統。可見Hdfs 在Hadoop 生態系統的獨特地位。

HDFS技術架構

HDFS 主要包含NameNode 和DataNode。

Namenode 又稱爲名稱節點,是負責管理分佈式文件系統的命名空間(Namespace ),保存了兩個核心的數據結構,即FsImage 和EditLog 。 你可以把它理解成大管家,它不負責存儲具體的數據。

FsImage 用於維護文件系統樹以及文件樹中所有的文件和文件夾的元數據。

操作日誌文件EditLog 中記錄了所有針對文件的創建、刪除、重命名等操作。

注意,這個兩個都是文件,也會加載解析到內存中。

爲啥會拆成兩個呢? 主要是因爲Fsimage 這個文件會很大的,多了之後就不好操作了,就拆分成兩個。把後續增量的修改放到EditLog 中, 一個FsImage 和一個Editlog 進行合併會得到一個新的FsImage .

因爲它是系統的大管家,如果這個玩意壞了,丟失了怎麼辦。就相當於你係統的引導區壞了。那就玩完了。整個文件系統就崩潰了。 所以,這個重要的東西,需要備份。

這個時候就產生了一個叫sendaryNamenode的節點用來做備份,它會定期的和Namenode 就行通信來完成整個的備份操作。具體的操作如下:

來源:https://hadoop.apache.org/

SecondaryNameNode的工作情況:

SecondaryNameNode 會定期和NameNode 通信,請求其停止使用EditLog文件,暫時將新的寫操作寫到一個新的文件edit.new上來,這個操作是瞬間完成,上層寫日誌的函數完全感覺不到差別;

SecondaryNameNode 通過HTTP GET 方式從NameNode 上獲取到FsImage 和EditLog 文件,並下載到本地的相應目錄下;

SecondaryNameNode 將下載下來的FsImage 載入到內存,然後一條一條地執行EditLog 文件中的各項更新操作,使得內存中的FsImage 保持最新;這個過程就是EditLog 和FsImage 文件合併;

SecondaryNameNode 執行完(3)操作之後,會通過post方式將新的FsImage 文件發送到NameNode 節點上;

NameNode 將從SecondaryNameNode 接收到的新的FsImage 替換舊的FsImage 文件,同時將edit.new 替換EditLog 文件,通過這個過程EditLog 就變小了;

除了這個自帶的備份操作,還需要進行人工的備份,把一份Fsimage 到多個地方進行備份,萬一Namenode 的節點壞了呢。

Datanode數據節點,用來具體的存儲文件,維護了BlockId 與 Datanode 本地文件的映射。 需要不斷地與Namenode 節點通信,來告知其自己的信息,方便Nameode來管控整個系統。

這裏還提到一個塊的概念,就像Linux本地文件系統中也有塊的概念一樣,這裏也有塊的概念。這裏的塊會默認是128m 每個塊都會默認儲存三份。

HDFS優缺點

HDFS 之所在Hadoop 生態系統中具有極其重要的地位,是因爲具有以下優點:

1、高容錯性

數據自動保存多個副本。它通過增加副本的形式,提高容錯性。
某一個副本丟失以後,它可以自動恢復,這是由 HDFS 內部機制實現的,我們不必關心。

2、適合批處理

它是通過移動計算而不是移動數據。
它會把數據位置暴露給計算框架。

3、適合大數據處理

處理數據達到 GB、TB、甚至PB 級別的數據。
能夠處理百萬規模以上的文件數量,數量相當之大。能夠處理10K 節點的規模。

4、流式文件訪問

一次寫入,多次讀取。文件一旦寫入不能修改,只能追加。
它能保證數據的一致性。

5、可構建在廉價機器上

它通過多副本機制,提高可靠性。
它提供了容錯和恢復機制。比如某一個副本丟失,可以通過其它副本來恢復。

同時HDFS也並非完美,有三種場景不適用:

1、低延時數據訪問

比如毫秒級的來存儲數據,這是不行的,它做不到。
它適合高吞吐率的場景,就是在某一時間內寫入大量的數據。但是它在低延時的情況下是不行的,比如毫秒級以內讀取數據,這樣它是很難做到的。

2、小文件存儲

存儲大量小文件(這裏的小文件是指小於HDFS系統的Block大小的文件(默認64M))的話,它會佔用 NameNode大量的內存來存儲文件、目錄和塊信息。

這樣是不可取的,因爲NameNode的內存總是有限的。小文件存儲的尋道時間會超過讀取時間,它違反了HDFS的設計目標。

3、併發寫入、文件隨機修改

一個文件只能有一個寫,不允許多個線程同時寫。
僅支持數據 append(追加),不支持文件的隨機修改。

HDFS 面臨的挑戰

HDFS 在2004年剛剛出現的時候,是一個劃時代的創舉。它讓企業第一次獲得了將存儲大規模數據集存儲到廉價計算機上的能力。這種能力奠定了Hadoop開源大數據技術發展的基礎,所以說HDFS技術是一項革命性的技術。

HDFS 從出現之日起,就保持快速的性能優化,功能迭代,讓它在Hadoop 生態系統裏一直佔據獨特的地位。

人們常說,打敗一個事物的往往不是跟它類似的事物,而是一個全新的事物。

近幾年來,隨着雲計算技術不斷完善,衆多雲存儲技術在成本上比HDFS 具有明顯的優勢。比如:AWS S3、Azure Blob Storage、谷歌雲存儲。1TB 的對象雲存儲沒約只需要20美元左右,而HDFS 每月需要花100 美元左右。

正因爲如此,谷歌的HDFS 服務只是將HDFS 操作轉換成對象雲存儲,在API 層面上兼容HDFS 接口方式存儲數據。

雲計算存儲技術除了在成本上具有巨大優勢以外,對於HDFS 不適應的場景,在設計之初就做了充分的考慮,並重點優化。相對於HDFS 有着後發的技術優勢。

總結

回到本節開頭提到的那個問題,HDFS 到底是先驅還是先烈呢?

毫無疑問,HDFS 作爲大數據存儲技術的開創者,肯定是先驅。儘管HDFS 乃至整個Hadoop 大數據技術在日益完善的雲技術面前並不佔優勢,但持續了十幾年Hadoop 大數據技術在全球範圍內有着衆多用戶。

這些用戶已經爲Hadoop 投入了巨大的人力和財力。目前來看Hadoop 技術生命力還會保持較長的一段時間。

關於HDFS 和Hadoop 未來的發展,你們怎麼看呢?

14 Spark 爲什麼這麼快

當你做成功一件事,千萬不要等待着享受榮譽,應該再做那些需要的事。
—— 巴斯德

上節我們講到了HDFS 的架構、原理以及雲計算對HDFS 的影響。這一小節我們來一起學習一下Spark 計算框架。帶着問題學習往往效率會比較高。這一小節我們將回答這個問題:Spark 爲什麼這麼快?

Spark到底有多快

spark官網旁邊有這樣的一段文字:

Run workloads 100x faster.

Apache Spark achieves high performance for both batch and streaming data, using a state-of-the-art DAG scheduler, a query optimizer, and a physical execution engine.

作爲Hadoop MapReduce 後繼者Apache Spark 可以支撐數千節點規模的集羣部署,尤其在內存數據處理上,Spark比MapReduce更加高效,且支持GB或TB級別的數據。

然而很多人都認爲在磁盤數據計算上,MapReduce 比Spark 更有優勢。2014年,大數據公司Databricks 爲了評估Spark 在PB 級磁盤數據計算的運行狀況, 其技術團隊使用 AWS進行了一個Daytona Gray 類別的排序基準測試。

測試結果顯示Spark 打破了MapReduce 保持的排序性能記錄。這次測試是一個考量系統排序100TB數據(約萬億條記錄)速度的行業基準測試。

在此之前,這項基準測試的世界記錄保持者是雅虎,他們使用2100 節點的MapReduce 集羣在72 分鐘內完成了計算。而本次測試Spark 只使用了206 個EC2 節點,就將排序用時縮短到了23 分鐘。

也就是說在相同數據的排序上,Spark 只使用了1/10 的計算資源就比MapReduce 快了近3 倍。

Spark爲什麼這麼快

Spark採用一種新的計算模型RDD。

RDD(Resilient Distributed Dataset)叫做彈性分佈式數據集,是Spark 中最基本的數據抽象,它代表一個不可變、可分區、裏面的元素可並行計算的集合。

Resilient:RDD 中的數據可以存儲在內存中或者磁盤中。

Distributed:RDD 中的數據是分佈式存儲的,可用於分佈式計算。

Dataset:一個數據集合,用於存放數據的。

RDD 具有數據流模型的特點:自動容錯、位置感知性調度和可伸縮性。RDD 允許用戶在執行多個查詢時顯式地將數據緩存在內存中,後續的查詢能夠重用這些數據,這極大地提升了查詢速度。

RDD 之間的依賴關係分類窄依賴(narrow dependency)和寬依賴(wide dependency, 也稱 shuffle dependency)。

窄依賴:是指每個父RDD 的一個Partition 最多被子RDD 的一個Partition 所使用,例如map、filter、union 等操作都會產生窄依賴,類比獨生子女。

寬依賴:是指一個父RDD 的Partition 會被多個子RDD 的Partition 所使用,例如groupByKey、reduceByKey、sortByKey 等操作都會產生寬依賴。類比超生。

相比於寬依賴,窄依賴對優化很有利,主要基於以下兩點:

寬依賴往往對應着shuffle 操作,需要在運行過程中將同一個父RDD 的分區傳入到不同的子RDD 分區中,中間可能涉及多個節點之間的數據傳輸;而窄依賴的每個父RDD 的分區只會傳入到一個子RDD 分區中,通常可以在一個節點內完成轉換。

當RDD 分區丟失時(某個節點故障),Spark 會對數據進行重算。

對於窄依賴,由於父RDD 的一個分區只對應一個子RDD 分區,這樣只需要重算和子RDD 分區對應的父RDD 分區即可,所以這個重算對數據的利用率是100% 的;
對於寬依賴,重算的父RDD 分區對應多個子RDD 分區,這樣實際上父RDD 中只有一部分的數據是被用於恢復這個丟失的子RDD 分區的,另一部分對應子RDD 的其它未丟失分區,這就造成了多餘的計算;

更一般的,寬依賴中子RDD 分區通常來自多個父RDD 分區,極端情況下,所有的父RDD 分區都要進行重新計算。

如下圖所示如果子RDD2 的2A 分區計算失敗,上游的父RDD1 的1A、1B、1C 三個分區都需要重新計算。會產生計算冗餘,1A、1B、1C 三個分區對應的2B 數據也重新計算了。

上面我們建立了Spark RDD 的概念,以及RDD之間的依賴關係。那麼整個Spark 程序是怎樣組織RDD之間的協作關係呢?

Spark 程序會使用一種叫DAG(Directed Acyclic Graph)有向無環圖的方式組織RDD邏輯關係。

在Spark程序中每個partition 分區是獨立計算的最小個體。當RDD之間的依賴關係是窄依賴的時候,子RDD的某個只需要等依賴上游父RDD對應的某個partion分區計算完畢之後即可開始計算下游計算邏輯,而不必等上游父RDD全部partition 計算完畢。

所以Spark 根據寬依賴將整個DAG 圖劃分成多個Stage ,每個Stage執行完畢之後會記錄一個CheckPoint 。當某個Stage 計算失敗的時候,只需要恢復到上個CheckPoint 狀態就可以了。

下面是Spark執行過程:

各個RDD之間存在着依賴關係,這些依賴關係就形成有向無環圖DAG ,DAGScheduler 對這些依賴關係形成的DAG進行Stage劃分,劃分的規則很簡單,從後往前回溯,遇到窄依賴加入本Stage ,遇見寬依賴進行Stage切分。

完成了Stage 的劃分。DAGScheduler 基於每個Stage 生成TaskSet ,並將TaskSet 提交給TaskScheduler。TaskScheduler 負責具體的task調度,最後在Worker節點上啓動task。

DAGScheduler

(1)DAGScheduler 對DAG 有向無環圖進行Stage劃分。

(2)記錄哪個RDD 或者 Stage 輸出被物化(緩存),通常在一個複雜的shuffle 之後,通常物化一下(cache 、persist ),方便之後的計算。

(3)重新提交shuffle 輸出丟失的stage(stage內部計算出錯)給TaskScheduler。

(4)將 Taskset 傳給底層調度器

a)– spark-cluster TaskScheduler

b)– yarn-cluster YarnClusterScheduler

c)– yarn-client YarnClientClusterScheduler

TaskScheduler

(1)爲每一個TaskSet 構建一個TaskSetManager 實例管理這個TaskSet 的生命週期。

(2)數據本地性決定每個task 最佳位置。

(3)提交 taskset ( 一組task ) 到集羣運行並監控。

(4)推測執行,碰到計算緩慢任務需要放到別的節點上重試。

(5)重新提交Shuffle輸出丟失的Stage 給DAGScheduler。

總結

我們來總結一下Spark計算速度快的原因:

1.高效DAG 有向無環圖將複雜的計算邏抽象成一個個RDD ,並按照RDD之間的依賴關係,劃分成多個Stage ,最大化地減少了重複計算。

2.RDD 豐富的計算算子可以匹配各種的計算場景,而不像MapReduce 需要將所有計算都轉化成Map 、Reduce 兩種計算模型。Spark RDD 的各個算子默認包含了大量的算法優化,RDD 計算效率對開發人員性能優化經驗的依賴大大降低。

3.Spark RDD 之間轉過過程中會優先使用內存緩存數據,相比MapReduce 計算模型Shuffle 階段一定要通過磁盤緩存數據的方式,執行效率要高效得多。

4.Spark通過CheckPoint 機制最大化地減少了偶然計算失敗導致的重複計算。

關於Spark 計算速度快的原因,你有哪些看法呢?

15 Strom、Spark streamming 、Flink 怎麼選

讀書而不思考,等於吃飯而不消化。
——波爾克

上一小節中我們講到了Spark相對於MapReduce在數據批處理性能上的有着巨大飛躍,這小節我們來講講實時計算引擎的技術選型。

實時計算引擎相對於批處理計算引擎領域來說可選擇的技術方案豐富很多,主流方案的包含Flink、Spark streamming,較老的Strom,比較小衆的 Kafka、Pulsar,還有Google的Beam,Intel 的Gearpump,IBM 的Edgent等等。

主流實時計算引擎總體對比

產品 Spark Streaming Storm Flink
架構 依賴spark生態,主從模式,每個Batch處理都依賴主(driver),可以理解爲時間維度上的spark DAG(有向五環圖)。 主從模式,且依賴zookeeper,處理過程中對主的依賴不大。 架構介於spark streaming和storm之間,主從結構與spark streaming相似,DataFlow Grpah與Storm相似,數據流可以被表示爲一個有向圖。 每個頂點是一個用戶定義的運算,每向邊表示數據的流動。
容錯 WAL及RDD 血統機制 Records ACK機制 基於Chandy-Lamport distributed snapshots checkpoint機制
延遲 處理的是一個事件窗口內的所有事件。具有秒級高延遲。 處理的是每次傳入的一個事件可實現亞秒級低延遲。 同Strom,單條事件處理可實現亞秒級低延遲
吞吐量 一般 一般 最高
數據處理保證 exactly once(實現採用Chandy-Lamport 算法,即marker-checkpoint ),可靠性較高。 at least once(實現採用record-level acknowledgments),Trident可以支持storm 提供exactly once語義。可靠性一般。 exactly once,可靠性較高
成熟度與活躍度 已經發展了一段時間,相對穩定 已經發展了一段時間,相對穩定。 新興項目,蓬勃發展中,社區活躍度一直在上升。
易用性 支持SQL Steaming,Batch和STREAMING採用統一編程框架。 不支持SQL Steaming 支持SQL Steaming,Batch和STREAMING採用統一編程框架。
適用場景 不是真正的實時流處理框架,有歷史包袱,但是其使用起來很簡單,且天然對接Spark生態棧中的其他組件,社區支持較大,吞吐量較大,部署也很簡單,UI也做的更加智能。所以,如果對延遲要求不高的情況下,建議直接使用Spark Streaming 即可。 大體上同 Spark Streaming,但是具備亞秒級延遲,速度上可以和 Flink 媲美,比起 Spark Streaming,其對實時性支持更好一些。但是在各方面的性能表現上,比 Flink 較差。 完全爲實時流處理而誕生,沒有歷史包袱。如果對延遲性要求比較高的話,那麼建議直接上 Flink。除了延遲較低外,其在API和容錯上也是做的比較完善,使用起來相對來說也是比較簡單的,部署容易,而且發展勢頭也越來越好。
Storm

Storm是一種較早期的實時計算引擎。像MapReduce 是專門爲數據批處理設計一樣,Storm 從一開始設計的時候就是爲實時計算而生的,一經推出一度成爲實時計算的工業標準。但是Storm 受限於當時歷史背景,很多方面的設計並不優雅。Storm 的Ack機制、exactly once 可靠性差。

在Storm 出現之前,進行實時處理是非常痛苦的事情,我們主要的時間都花在關注往哪裏發消息,從哪裏接收消息,消息如何序列化,真正的業務邏輯只佔了源代碼的一小部分。

Storm 出現之後,人們具備了處理實時計算的能力,但是Storm exactly once 可靠性差、Ack 開啓之後性能差的問題,讓Storm 的應用範圍侷限在Lambda 架構之上處理小數據量的實時計算問題。

來源Storm官網:http://storm.apache.org/

Storm 主要架構有Nimbus、ZooKeeper、Supervisor 組成。

Nimbus:負責在集羣裏面發送代碼,分配工作給機器並且監控狀態,在集羣中只有一個,作用類似Hadoop 裏面的JobTracker。

ZooKeeper:Storm 重點依賴的外部資源,Nimbus、Supervisor 和Worker 等都是把心跳數據保存在ZooKeeper上,Nimbus 也是根據ZooKeeper上的心跳和任務運行狀況進行調度和任務分配的。

Supervisor:在運行節點上,監聽分配的任務,根據需要啓動或關閉工作進程Worker。每一個要運行Storm 的機器上都運行一個Supervisor,並且按照機器的配置,設定上面分配的槽位數。

Topology:Storm 提交運行的程序稱爲Topology,它是由Spout 和Bolt 構成,Spout 是發出Tuple 的結點,Bolt可以隨意訂閱某個Spout 或者Bolt 發出的Tuple。

Spout:Spout 是一個Topology 的消息生產的源頭,Spout 生產的消息在Storm 中被抽象爲Tuple,在整個Topology 的多個計算組件之間都是根據需要抽象構建的Tuple 消息來進行連接,從而形成流。實時數據源的處理邏輯在spout 裏配置。

Bolt:Storm中消息的處理邏輯被封裝到Bolt組件中,任何處理邏輯都可以在Bolt 裏面執行,實時數據處理的業務邏輯寫在Bolt裏。

下圖的表格列舉了Storm 和Hadoop 概念的對應關係,熟悉Hadoop 的同學應該能夠很好的理解了。

Storm Hadoop
Nimbus JobTracker
Supervisor TaskTracker
Topology Job
Spark Streamming

Spark Streamming處理實時數據的思路跟Strom 有很大的不同,Spark Streamming 將無限實時數據集分割成小的數據集,基於這些小的數據集,進行微批處理計算。

Spark Streaming 有高吞吐量和容錯能力強這兩個特點。Spark Streaming 支持的數據輸入源很多,例如:Kafka、Flume、Twitter、ZeroMQ 和簡單的 TCP 套接字等等。

數據輸入後可以用 Spark 的高度抽象原語如:map、reduce、join、window 等進行運算。而結果也能保存在很多地方,如 HDFS,數據庫等。另外 Spark Streaming 也能和 MLlib(機器學習)以及 Graphx 完美融合。

實時無限數據集輸入以後會分割成多個批處理的數據,批處理的數據然後經過一系列的加工邏輯,然後會被輸出成流處理的結果。

Spark Streaming 提供了一種叫DStream 的 API 處理實時數據流。DStream 將Spark Streaming 處理數據的底層實現的細節做了高度抽象,用戶只需要瞭解DStream 的算子即可處理實時無限數據集。

DStream 目前有微批和流式兩個處理引擎。微批提供了窗口、消息時間、trigger、watermarker、流表 join、流流 join 等常見的流處理能力,時延仍然保持最小 100 毫秒。

DStream 的流式引擎還處在實驗階段,雖然延遲上做到了1毫秒,不支持 exactly-once 語義,只支持 at-least-once 語義。

DStream 底層的數據處理操作都是基於RDD 實現的,Spark RDD 身上具備的高容錯性、數據處理高效性優點都被DStream 繼承了。

上面的圖展示了一個DStream 的flatMap 算子的實現原理。DSteam 的數據流按照時間進行分割成多個小的數據集,每個數據集通過flatMap 算子轉換成新的數據集,這些新的數據集也是基於時間排序,最終轉換成一個新的DStream。

Flink

Flink 項目由德國柏林理工大學教授 Volker Markl 發起,他參考了谷歌的流計算最新論文 MillWheel,決定以流計算爲基礎,開發一個流批結合的分佈式流計算引擎 Flink。Flink 於 2014 年 3 月進入 Apache 孵化器並於 2014 年 11 月畢業成爲 Apache 頂級項目。

Flink 處理數據的設計思路是流批合一,以流爲基礎,批是流的特例或上層 API。

Flink 採用 Dataflow 模型,和 Lambda 模式不同。Dataflow 的邏輯關係和運行時的物理拓撲相差不大,是純粹的流式設計,時延和吞吐理論上是最優的。

Flink 在流批計算上沒有包袱,一開始就走在對的路上。

Flink 的架構上由JobManagers、TaskManagers、Clients組成。

當用戶提交一個 Flink 程序時,會首先創建一個 Client,該 Client 首先會對用戶提交的 Flink 程序進行預處理,並提交到 Flink 集羣中處理。

Client 會將用戶提交的Flink程序組裝一個JobGraph, 並且是以JobGraph 的形式提交的。一個JobGraph 是一個Flink Dataflow,它由多個JobVertex 組成的DAG。其中,一個JobGraph 包含了一個Flink 程序的如下信息:JobID、Job名稱、配置信息、一組JobVertex等。

JobManagers (也稱爲 masters)協調分佈式計算。它們負責調度任務、協調 checkpoints、協調故障恢復等。

每個 Job 至少會有一個 JobManager。高可用部署下會有多個 JobManagers,其中一個作爲 leader,其餘處於 standby 狀態。

TaskManagers(也稱爲 workers)執行 dataflow 中的 tasks(準確來說是 subtasks ),並且緩存和交換數據 streams。

每個 TaskManager(worker)都是一個 JVM 進程,並且可以在不同的線程中執行一個或多個 subtasks。爲了控制 worker 接收 task 的數量,worker 擁有所謂的 task slots (至少一個)。

每個 task slots 代表 TaskManager 的一份固定資源子集。

通過調整 slot 的數量,用戶可以決定 subtasks 的隔離方式。每個 TaskManager 有一個 slot 意味着每組 task 在一個單獨的 JVM 中運行(例如,在一個單獨的容器中啓動)。

擁有多個 slots 意味着多個 subtasks 共享同一個 JVM。 Tasks 在同一個 JVM 中共享 TCP 連接(通過多路複用技術)和心跳信息(heartbeat messages)。它們還可能共享數據集和數據結構,從而降低每個 task 的開銷。

默認情況下,Flink 允許 subtasks 共享 slots,即使它們是不同 tasks 的 subtasks,只要它們來自同一個 job。

通過 slot sharing,將示例中的並行度從 2 增加到 6 可以充分利用 slot 的資源,同時確保繁重的 subtask 在 TaskManagers 之間公平地獲取資源。

總結
Flink 基於流的流批一體的思路,相對於Spark Streaming 基於批的批流一體設計,在設計思路上就領先一大步,直接的收益就是Flink 處理數據延遲在亞秒級,小於Spark Streaming 秒級的延遲。同時在數據處理吞吐量上面相對於Storm 有着巨大的優勢,同時由於Flink高可靠的exactly once設計,Flink 在數據精確去重場景下對於Storm 有着明顯的優勢。

16 Hbase:列式存儲利器

人要有毅力,否則將一事無成。
——居里夫人

上一節我們一起學習實時計算引擎的技術選型,這一節來學習Hbase 數據庫。

大家都知道Google 通過GFS、MapReduce、Big Table "三駕馬車"開創了大數據技術的先河。這三篇論文像一座燈塔照亮了開源大數據技術行業的發展。

基於GFS 的設計,產生了HDFS 分佈式文件系統,用於大數據的文件存儲。基於MapReduce,產生了Java 版本的MapReduce,用做大數據的計算引擎。而基於Big table,產生了Hbase 分佈式數據庫,用於大數據的查詢。
圖片描述

HBase是什麼

HBase 是一個高可靠、高性能、面向列、可伸縮的分佈式NoSQL 數據庫,運行於HDFS 文件系統之上,主要用來存儲非結構化和半結構化的鬆散數據。

HBase 的目標是處理非常龐大的表,可以通過水平擴展的方式,利用廉價計算機集羣處理由超過10 億行數據和數百萬列元素組成的數據表。

HBase VS HDFS

HDFS 本質上是一個數據文件系統,對於數據批量查詢的場景有着很好的支持,但對於數據隨機查詢上比較無力。同時HDFS不支持數據的更新、不合適增量數據處理。爲了解決大規模數據的隨機查詢問題,HBase 應運而生。

HBase VS RDBMS

一直以來人們都在使用傳統的關係型數據庫(RDBMS)解決數據寫入、數據查詢問題。關係型數據庫在解決數據查詢上的表現是久經考驗的,具有輕量化、高可靠性、索引支持良好的優點,並支持事務特性。

但是在大數據場景下,RDBMS 查詢效率非常低。例如RDBMS 裏面應用廣泛的Mysql 數據庫,單個數據表的數據量超過千萬級以後數據查詢效率會直線下降。然而在大數據場景下,單個數據表數據量過億的場景都非常常見。

Hbase RDBMS
數據庫大小 PB GB、TB
數據類型 Bytes 豐富的數據類型
事務支持 ACID只支持單個Row級別 全面的ACID支持ROW和表
索引 只支持ROW-Key 支持
吞吐量 百萬查詢/秒 數萬查詢/秒
正因爲RDBMS 不能滿足大數據場景下數據查詢性能要求,數據庫科學家們一直在嘗試探索新的數據庫系統。直到人們突破關係型數據庫思維束縛,創造出非關係型數據庫,也就是Nosql 數據庫之後,一切問題迎刃而解。

Hbase 正是基於Nosql 的設計思想,並添加列式存儲、高效的數據模型設計,針對大數據場景做了優化,一經推出以後迅速佔領了工業級大數據查詢領域的市場。

由於Hbase 的巨大影響力,一個名叫Phoenix 項目實現在Hbase 之上使用SQL 語法查詢數據。Phoenix 最終進入Apache 基金會,成爲了頂級的開源項目。

藉助Phoenix 對於SQL查詢語法的支持,Hbase 的數據查詢使用門檻被進一步降低。

Hbase架構

Hbase 是基於Zookeeper、HDFS 構建。Hbase自身的主要組件有:HMaster、Region Server。
圖片描述

Zookeeper:

Zookeeper 是HBase HA 的解決方案,是整個集羣的協調器通過Zookeeper 保證了至少有一個HMaster 處於active 狀態HMaster 並不直接參與數據的讀寫操作。

當我們使用HBase 的API 的時候,當我們想用HBase 的API 去讀取數據的時候,我們並不需要知道HMaster 的地址、也不需要知道RegionServer 的地址,我們只需要知道Zookeeper 集羣的地址就可以了

HMaster 啓動將系統加載到Zookeeper

Zookeeper 保存了HBase 集羣region 的信息、meta 的信息等等

維護着RegionServer 的狀態信息,知道哪些數據需要從哪些RegionServer 去讀取

HMaster:

HMaster 是HBase 主/從集羣架構中的中央節點
HMaster 用於協調多個RegionServer、檢測各個RegionServer 的狀態、並且平衡各個RegionServer 之間的負載、同時還負責分配region 到RegionServer
region:region 是HBase 中存儲的最小的單元、是HBase 表格的基本單位
HMaster 維護表和Region 的元數據,不參與數據的輸入/輸出過程
HBase 本身是支持HA 的,也就是說同時可以有多個HMaster 進行運行,但是隻有1個處於active 狀態
如果處於active 的節點失效了,掛掉了,其它的HMaster 節點就會選舉出一個active 節點來接管整個HBase 集羣
Region Server:

Region Server 負責數據的讀寫,數據存放在內存中,持續化需要和HDFS 文件系統進行I/O 交互。

HBase 是列族數據庫,列的數據是存放在一起的,不同的行按照row key 分佈,存儲在不同的Region Server中。

HDFS:

數據的存儲與備份。

將數據存儲在HDFS 的一個顯而易見的好處時,當集羣Region Server 發生變化時,增加或者減少時,不需要在節點間進行數據的複製,這大大減少了節點的上下線時間,和I/O 消耗。

Hbase數據模型

在Hbase中數據被存在Table 中的,但是Hbase 的Table 跟RDBMS 裏面的Table 有很大的不同,Hbase 的Table 是一個多維的Map。Hbase 數據模型的邏輯視圖是由下面概念組成的。

Row:在表裏面,每一行代表着一個數據對象,每一行都是以一個Row Key 來進行唯一標識的,Row Key 並沒有什麼特定的數據類型,以二進制的字節來存儲

Column:HBase 的列由Column family 和Column qualifier 組成,由冒號(:)進行間隔。

比如 family:qualifier

RowKey:可以唯一標識一行記錄,不可被改變。改變的唯一方式是刪除這個RowKey,再重新插入

Column Family:是一些Column 的集合,1個Column Family 所包含的所有的Column 成員是有着相同的前綴。在物理上1個Column Family 所有的成員是存儲在一起的,存儲的優化都是針對Column Family 級別的。

這就意味着1個- Column Family 的成員都是用相同的方式進行訪問的。在定義HBase 表的時候需要提前設置好列族,表中所有的列都需要組織在列族裏面;列族一旦定義好之後,就不能輕易的更改了,因爲它會影響到HBase真實的物理存儲結構。

Column Qualifier:列族中的數據通過列標識(Column Qualifier)來進行映射,可以理解爲一個鍵值對,Column Qualifier 就是key

Cell:每一個RowKey、Column Family、Column Qualifier 共同組成的一個單元。存儲在Cell 裏面就是我們想要保存的數據。Cell存儲的數據沒有特定的數據類型,以二進制字節來進行存儲

Timestamp:每個值都會有一個timestamp,作爲該值特定版本的標識符。默認HBase 中每次插入數據的時候,都會用timestamp 來進行版本標識。讀取數據時,如果這個時間戳沒有被指定,就默認返回最新的數據。

寫入數據時,如果沒有設置時間戳,默認使用當前的時間戳。每一個列族的數據的版本都由HBase 單獨維護。默認情況下,HBase 會保留3 個版本的數據

Row Key Time Stamp ColumnFamily contents ColumnFamily anchor ColumnFamily people
“com.cnn.www” t9 anchor:cnnsi.com = “CNN”
“com.cnn.www” t8 anchor:my.look.ca = “CNN.com”
“com.cnn.www” t6 contents:html = “…”
“com.cnn.www” t5 contents:html = “…”
“com.cnn.www” t3 contents:html = “…”
“com.example.www” t5 contents:html = “…” people:author = “John Doe”
上面是一個實際Hbase 邏輯視圖的例子。每一行有一個唯一的row key,每個Row key對應着三個Column Family,每個ColumnFamily 是有一個後者多個key-value 對組成的。這些key就是Column Qualifier,Value 則以Cell 保存的數據。邏輯視圖表示成Map 的形式是下面這樣的。

{
  "com.cnn.www": {
    contents: {
      t6: contents:html: "<html>..."
      t5: contents:html: "<html>..."
      t3: contents:html: "<html>..."
    }
    anchor: {
      t9: anchor:cnnsi.com = "CNN"
      t8: anchor:my.look.ca = "CNN.com"
    }
    people: {}
  }
  "com.example.www": {
    contents: {
      t5: contents:html: "<html>..."
    }
    anchor: {}
    people: {
      t5: people:author: "John Doe"
    }
  }
}

雖然Hbase 邏輯視圖上,多個ColumnFamily 和Row Key 組成一個Row(行),但是ColumnFamily 物理上是分開存儲的。下面兩張表是Hbase 數據模型實際的存儲形式。

Row Key Time Stamp Column Family anchor
“com.cnn.www” t9 anchor:cnnsi.com = “CNN”
“com.cnn.www” t8 anchor:my.look.ca = “CNN.com”
Row Key Time Stamp ColumnFamily contents:
“com.cnn.www” t6 contents:html = “…”
“com.cnn.www” t5 contents:html = “…”
“com.cnn.www” t3 contents:html = “…”
Hbase 的Column Family 是包含在Table Schema 信息裏面的,需要提前定義好。Column Qualifier 不是Schema信息裏面的,可以隨時添加新的Column Qualifier 到Hbase 的Table 當中。

總結

Hbase 是基於Nosql 的Key-Value 數據模型,獨特的列式存儲方式對於大數據場景下的數據查詢有着極高的效率,同時寬鬆的ColumnFamily 設計,擺脫了傳統RDBMS 嚴格的Scheme 束縛,更好的適應了大數據場景下數據模型的快速更新需求。

Phoenix 項目的出現讓開發人員可以使用SQL 語言的方式查詢Hbase 的數據,讓Hbase 成了Hadoop 大數據技術最重要的組成部分。

17 Yarn:資源調度中樞

什麼是路?就是從沒路的地方踐踏出來的,從只有荊棘的地方開闢出來的。 
—— 魯迅

上一小節我們學習了Hbase 數據庫,這一小節我們來一起學習Yarn。

YARN的發展歷史

Hadoop 在框架演進的過程中經歷了多個階段,其中主要包括四個階段:Ad Hoc (點對點)集羣時代(單用戶)、Hadoop on Demand(HOD) 時代(以通用系統形式,在共享集羣中部署私有Hadoop MapReduce 和HDFS 實例)、共享集羣黎明時代( JobTracker 和 TaskTracker 的誕生)、YARN 時代。

在這個過程中Hadoop 碰到了需要需要解決的問題,而YARN 就是爲了解決這些具體問題而設計的。

YARN 需要解決的問題:

1、可擴展性:下一代計算平臺應該可以平滑地擴展到數萬個節點和併發的應用。

2、可維護性:下一代計算平臺應該保證集羣升級與用戶應用程序的完全解耦。

3、多租戶:下一代計算平臺需要支持一個集羣中多個租戶並存,同時支持多個租戶之間細粒度地共享單個節點。

4、位置感知:對很多應用來說,將計算移動到數據所在的位置是一個重大的進步。

5、高集羣使用率:下一代計算平臺底層物理資源的高使用率。

6、安全和可審計操作:下一代計算平臺繼續以安全的、可審計的方式使用集羣資源

7、可靠性和可用性:下一代計算平臺應該有高度的可靠的用戶交互,並支持高可用性。

8、對編程模型多樣性的支持:下一代計算平臺必須支持多樣化的編程模型,需要演進不僅僅以MapReduce 爲核心。

9、靈活的資源模型:下一代計算平臺支持各個節點的動態資源配置以及靈活的資源模型。

10、向後兼容:下一代計算平臺應該保持現有MapReduce 應用程序的完全向後兼容。

YARN 誕生之前計算資源的管理是在MapReduce 中實現的,JobTracker 和TaskTracker 承擔了MapReduce 計算過程中計算資源的調度管理任務。YARN 誕生之後計算資源的管理功能被單獨抽離出來。

YARN 從設計之初就將兼容編程模型多樣化作爲核心要點,不僅可以支持MapReduce On Yarn,還可以兼容其他後出現的計算引擎。

因爲Yarn較爲優秀的通用性,MapReduce 之後出現的Spark、Flink 都可以通過Yarn 管理計算資源,YARN 成爲了應用最爲廣泛的通用資源調度系統。

如果你的應用程序需要使用YARN 的資源管理功能,也可以實現YARN 提供的API,來將應用運行在YARN 之上,將資源分配和回收統一交給YARN 去管理,大大簡化資源管理功能的開發。

JobTracker和TaskTracker架構

JobTacker其承擔的任務有:接受任務、計算資源、分配資源、與DataNode 進行交流。

在hadoop中每個應用程序被表示成一個作業,每個作業又被分成多個任務,JobTracker 的作業控制模塊則負責作業的分解、TaskTracker 狀態監控、作業狀態監控、任務狀態監控。

TaskTracker 是JobTracker 和Task 之間的橋樑。

一方面,從JobTracker 接收並執行各種命令:運行任務、提交任務、殺死任務等;另一方面,將本地節點上各個任務的狀態通過心跳週期性彙報給JobTracker。TaskTracker 與JobTracker 和Task 之間採用了RPC 協議進行通信。

JobTracker 和TaskTracker 組成的資源調度架構缺點比較多:

JobTracker 是集羣事務的集中處理點,存在單點故障

JobTracker 需要完成的任務太多,既要維護job 的狀態又要維護job 的task 的狀態,造成過多的資源消耗

在taskTracker 端,用map/reduce task 作爲資源的表示過於簡單,沒有考慮到CPU、內存等資源情況,當把兩個需要消耗大內存的task 調度到一起,很容易出現OOM

把資源強制劃分爲map/reduce slot ,當只有map task 時,reduce slot 不能用。當只有reduce task 時,map slot 不能用,容易造成資源利用不足。

YARN架構

Yarn的架構主要由:Resource Manager、Node Manager、Application Mater、Container組成。

YARN最基本的想法是將原JobTracker 主要的資源管理和job調度/監視功能分開作爲兩個單獨的守護進程。

有一個全局的ResourceManager(RM )和每個Application 有一個ApplicationMaster(AM),Application map-reduce job 或者DAG jobs。

ResourceManager 和 NodeManager(NM) 組成了基本的數據計算框架。ResourceManager協調集羣的資源利用,任何client 或者運行着的applicatitonMaster 想要運行job 或者task 都得向RM 申請一定的資源。

RM使用resource container 概念來管理集羣的資源,resource container 是資源的抽象,每個container 包括一定的內存、IO、網絡等資源,不過目前的實現只包括內存一種資源。

ApplicationMaster

ApplicationMaster 首先是一個框架庫,它的功能在官網上說的不夠系統,大意,由於NodeManager 執行和監控任務需要資源,所以通過ApplicationMaster 與ResourceManager 溝通,獲取資源。換句話說,ApplicationMaster 起着中間人的作用。

轉換爲更專業的術語:AM負責向ResourceManager索要NodeManager執行任務所需要的資源容器,更具體來講是ApplicationMaster 負責從Scheduler 申請資源,以及跟蹤這些資源的使用情況以及任務進度的監控。

對於MapReduce 框架而言有它自己的AM 實現,用戶也可以實現自己的AM,在運行的時候,AM 會與NM 一起來啓動和監視tasks。

NodeManager

NodeManager 是基本的計算框架。NodeManager 是客戶端框架負責 containers, 監控他們的資源使用 (cpu, 內存, 磁盤, 網絡) 和上報給 ResourceManager/Scheduler。

在啓動container 的時候,NM會設置一些必要的環境變量以及將container 運行所需的jar 包、文件等從hdfs 下載到本地,也就是所謂的資源本地化。

當所有準備工作做好後,纔會啓動代表該container 的腳本將程序啓動起來。啓動起來後,NM會週期性的監視該container 運行佔用的資源情況,若是超過了該container 所聲明的資源量,則會kill掉該container 所代表的進程。

另外,NM 還提供了一個簡單的服務以管理它所在機器的本地目錄。Applications 可以繼續訪問本地目錄即使那臺機器上已經沒有了屬於它的container 在運行。例如,Map-Reduce 應用程序使用這個服務存儲map output 並且shuffle 它們給相應的reduce task。

在NM 上還可以擴展自己的服務,YARN提供了一個yarn.nodemanager.aux-services 的配置項,通過該配置,用戶可以自定義一些服務,例如Map-Reduce 的shuffle 功能就是採用這種方式實現的。

ResourceManager

ResourceManager 有兩個組件:調度器(Scheduler)和應用程序管理器(ApplicationsManage)。

調度器(Scheduler)是可插拔的,比如有Fair Scheduler、Capacity Scheduler等,當然調度器也可以自定義。

Scheduler 負責分配最少但滿足application 運行所需的資源量給Application。

Scheduler 只是基於資源的使用情況進行調度,並不負責監視/跟蹤application 的狀態,當然也不會處理失敗的task。

ApplicationsManager 負責處理client 提交的job 以及協商第一個container 以供applicationMaster 運行,並且在applicationMaster 失敗的時候會重新啓動applicationMaster。

ApplicationsManager 負責系統中所有AM的生命週期的管理。ApplicationsManager 負責AM ,當AM 啓動後,AM會週期性的向ApplicationsManager 發送heartbeat,默認是1s,AsM 據此瞭解AM 的存活情況,並且在AM 失敗時負責重啓AM。

若是一定時間過後(默認10分鐘)沒有收到AM 的heartbeat,AsM 就認爲該AM 失敗了。

Container

Container 是YARN 的資源抽象,它封裝了某個節點中的內存、CPU、磁盤、網絡等,當ApplicationMaster 向ResourceManager申請資源的時候,RM向AM分配的資源就是通過Container 表示的。

YARN 會爲每個任務分配一個Container,並且該任務只能使用自己Container 中的資源。Container 是一個動態的資源劃分,根據應用程序的需求動態生成的。它不同於MRv1中的slot。

目前爲止YARN 僅支持CPU 和內存兩種資源,並且使用的是Cgroups 輕量級資源隔離機制進行隔離。

Yarn調度器與隊列

在YARN中,調度器是一個可插拔的組件,常見的有FIFO,CapacityScheduler,FairScheduler。可以通過配置文件選擇不同的調度器。

在RM端,根據不同的調度器,所有的資源被分成一個或多個隊列(queue),每個隊列包含一定量的資源。用戶的每個application,會被唯一的分配到一個隊列中去執行。

隊列決定了用戶能使用的資源上限。所謂資源調度,就是決定將資源分配給哪個隊列、哪個application 的過程。

可見調度器的兩個主要功能:1.決定如何劃分隊列;2.決定如何分配資源。此外,還有些其他的特性:ACL、搶佔、延遲調度等等

總結

在YARN 的架構設計中,JobTracker 的功能被分散到各個進程中包括ResourceManager 和NodeManager:
比如監控功能,分給了NodeManager,和Application Master。

ResourceManager 裏面又分爲了兩個組件:調度器及應用程序管理器。也就是說YARN 重構後,JobTracker 的功能,被分散到了各個進程中。同時由於這些進程可以被單獨部署所以這樣就大大減輕了單點故障,及壓力。

18 Kafka:數據傳輸管道

我們有力的道德就是通過奮鬥取得物質上的成功;這種道德既適用於國家,也適用於個人。
——羅素

這一節我們一起來學習一下數據傳輸管道:Kafka。

人們每天的日常生活會產生大量的數據,這些數據散落在各個角落,我們需要一套高效的數據傳輸工具來將這些數據集中起來。數據從產生到被集中收集的過程,就像下雨的時候雨水從城市街道里面的下水管道流入污水處理中心的過程一樣。

由於數據量龐大,數據傳輸過程中既要保證不丟數據,又要保證下游數據讀取數據時,數據能夠被快速讀取,數據傳輸工具需要設計的足夠高效。另一方面。數據的產生速度是不穩定的,數據的產生量存在波峯和波谷,數據傳輸工具需要有”削峯填谷“的能力。

基於上面的需求而設計的數據傳輸工具通常被稱之爲消息系統。

點對點 VS 發佈/訂閱

消息系統根據消息被消費的特點分爲:點對點模式(Peer-to-Peer)、發佈/訂閱模式。

點對點模式

消息生產者生產消息發送到queue 中,然後消息消費者從queue 中取出並且消費消息。這裏要注意:

消息被消費以後,queue 中不再有存儲,所以消息消費者不可能消費到已經被消費的消息。
Queue支持存在多個消費者,但是對一個消息而言,只會有一個消費者可以消費。
發佈/訂閱模式

消息生產者(發佈)將消息發佈到topic 中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,發佈到topic 的消息會被所有訂閱者消費。

上面圖中上半部分是一個點對點消息系統,Producer1 產生的消息放入消息系統中,Consumer1 消費了這個消息,其他的Consumer 不再能消費到這個消息了。上圖中的下半部分是一個發佈/訂閱消息提供。Pushlisher1 發佈的消息放入到消息系統之後,下游多個訂閱者(Subscriber1~SubscriberN)都可以消費到這個消息。

發佈/訂閱模式的消息系統相對於點對點模式的消息系統,在消息消費模式上更加靈活,可以適應更加複雜的數據傳輸場景。

在大數據場景中,數據的生產和消費關係非常複雜,基於發佈/訂閱模式的消息系統被廣泛運用於工業級大數據生產中。

Why Kafka

開源的消息系統非常多,有ActiveMQ、RabbitMQ、Kafka、RocketMQ。主要從以下幾個維度進行對比:社區活躍度、客戶端支持、吞吐量、延遲、數據可靠性。

數據可靠性 延遲 單機吞吐 社區 客戶端
ActiveMQ 中 / 萬級 不太活躍 支持全面
RabbitMQ 高 微秒級 萬級 活躍 支持全面
Kafka 高 毫秒級 十萬 活躍 支持全面
RocketMQ 高 毫秒級 十萬 有待加強 待加強
Kafka 通過Consumer Group 的方式實現了同時支持點對點模型和發佈/訂閱模式傳遞消息。

Kafka背景

kafka 是最初由Linkedin 公司開發,使用Scala 語言編寫,Kafka 是一個分佈式、分區的、多副本的、多訂閱者的日誌系統(分佈式MQ系統),可以用於web/nginx 日誌,搜索日誌,監控日誌,訪問日誌等等。

kafka 目前支持多種客戶端語言:java,python,c++,php等等。

Kafka架構

Kafka 架構主要由:Producer、Broker、Consumer、Zookeeper 組成。

Broker:Kafka 集羣包含一個或多個服務器,這種服務器被稱爲broker。

Producer:負責發佈消息到Kafka broker。

Topic:每條發佈到Kafka集羣的消息都有一個類別,這個類別被稱爲topic。(物理上不同topic 的消息分開存儲,邏輯上一個topic 的消息雖然保存於一個或多個broker上但用戶只需指定消息的topic 即可生產或消費數據而不必關心數據存於何處。

Partition:parition 是物理上的概念,每個topic 包含一個或多個partition,創建topic 時可指定parition 數量。每個partition 對應於一個文件夾,該文件夾下存儲該partition 的數據和索引文件。

Consumer:消費消息。每個consumer 屬於一個特定的consumer group(可爲每個consumer指定group name,若不指定group name 則屬於默認的group )。使用consumer high level API 時,同一topic 的一條消息只能被同一個consumer group內的一個consumer 消費,但多個consumer group 可同時消費這一消息。

Consumer Group (CG):若干個Consumer 組成的集合。這是kafka 用來實現一個topic 消息的廣播(發給所有的consumer)和單播(發給任意一個consumer)的手段。一個topic 可以有多個CG。

topic 的消息會複製(不是真的複製,是概念上的)到所有的CG,但每個CG 只會把消息發給該CG 中的一個consumer 。如果需要實現廣播,只要每個consumer 有一個獨立的CG 就可以了。

要實現單播只要所有的consumer 在同一個CG。用CG 還可以將consumer 進行自由的分組而不需要多次發送消息到不同的topic。

Kafka設計上的亮點

kafka 之所以被廣泛運用到大數據工業級生產環境跟它本身非常多的設計亮點是分不開的。

高可用(High available)

Kafka通過replica 和isr 機制來保證數據的高可用。要了解這兩個機制的原理,我們需要先了解一下Kafka 數據模型分層設計。

Kafka的數據模型上分爲Topic(主題)-Partition(分區)-Message(消息)三層。

一個Topic對應多個Partition,replica(多副本機制)是對Patition 而言的,每個Partition 的副本個數N是可以配置的。對於一個Topic 的某個Patition 來說,會有一個replica 爲leader,其他都爲follower,leader 處理對這個partition 的所有讀寫請求,與此同時,follower 會被動地去複製leader 上的數據。

Kafka 提供日誌複製算法保證,如果leader 發生故障或掛掉,一個新leader 被選舉並且客戶端的消息成功寫入。Kafka 確保從isr (同步副本列表)中選舉一個副本爲leader ,或者換句話說,follower 追趕leader 日誌。leader 負責跟蹤isr (同步副本列表)中所有follower 滯後狀態。

當生產者發送一條消息到Broker ,leader 寫入消息並複製到所有follower。消息提交之後才被成功複製到所有的同步副本。消息複製延遲受最慢的follower限制,重要的是快速檢測慢副本,如果follower "落後"太多或者失效,leader 將會把它從isr(同步副本列表)中移除。

Kafka 分區的作用主要是提供負載均衡的能力,即實現系統的高伸縮性。

Kafka 的replica(副本機制)和isr(同步副本列表)則保障了高可用性。

高性能(High performance)

Kafka 單機的吞吐量可以達到十萬級,通過sendfile 和pagecache 來實現zero copy 機制,順序讀寫的特性使得用普通磁盤就可以做到很大的吞吐,相對來說性價比比較高。

pagecache(頁緩存)是操作系統實現的一種主要的磁盤緩存,用來減少對磁盤I/O 的操作。具體就是把磁盤中的數據緩存到內存中,把對磁盤的訪問變成對內存的訪問。

所謂的zero copy(零拷貝)是指將數據直接從磁盤文件複製到網卡設備中,而不需要經由應用程序之手。

Kafka在讀取文件的時候大量時候pagecache(頁緩存),減少了對磁盤的讀取,從內存讀取數據,另一方面使用zero copy(零拷貝)技術將內存中讀取到的數據直接複製到網卡設備中,進一步提高了數據讀取的性能。

Linux 操作系統而言,零拷貝技術依賴於底層的sendfile() 方法實現。對應於Java 語言,FileChannal.transferTo()的底層實現就是sendfile()。

容錯(fault tolerance)

Kafka 的容錯主要通過controller 和coordinator 實現的。

controller 主要是做集羣的管理。coordinator 主要做業務級別的管理。這兩種角色都由Kafka裏面的某個broker來擔任,這樣failover 就很簡單,只需要選一個broker 來替代即可。

從這個角度來說Kafka 有一個去中心化的設計思想在裏面, 但controller 本身也是一個瓶頸,可以類比於hadoop的namenode 。

總結

消息系統有點對點模式(Peer-to-Peer)、發佈/訂閱模式,Kafka 通過Consumer Group 的方式實現了對這兩種消息傳遞方式的支持。由於Kafka 諸多優秀的設計,使得kafka 具備高可用(High available)、高性能(High performance) 以及足夠的容錯(fault tolerance)能力。

然而kafka 也並非完美,在Kafka 集羣上的主題(Topic)很多的時候,Kafka 集羣的性能就會大幅度下降,正是由於這樣的問題,一個名叫Pulsar 的新一代消息系統開始逐漸發展起來。

19 Flume:數據採集的爪子

成功=艱苦的勞動+正確的方法+少談空話。
——愛因斯坦

上一小節中我們介紹了數據傳輸中需要使用消息系統來緩存數據,實現” 削峯填谷 “。這一小節我們來學習一下數據採集的爪子:Apache Flume。

我們爲什麼需要 Flume

我們有了消息系統以後,數據可以緩存到消息系統裏面,供下游消費。但是在實際的生產場景中,需要蒐集的數據通常會有兩種形態出現。一種是以結構化數據的形式存儲在服務器的數據庫中,比如 Mysql 數據庫。另外一種形態是以後臺服務器的日誌的形式出現的。

這些散落在服務器的日誌,通常以日誌文件的形式存儲一個在固定的日誌文件夾中。對於一個超大型互聯網公司來說,服務器的數量非常龐大,可以達到驚人的萬臺以上。這麼多的服務器日誌文件怎麼才能蒐集到消息系統當中呢?

這時候就需要一個分佈式的日誌蒐集工具。

Apache Flume 是一個分佈式、高可靠、高可用的用來收集、聚合、轉移不同來源的大量日誌數據到中央數據倉庫的工具。

Apache Flume 是 Apache 軟件基金會(ASF)的頂級項目。

Flume 架構

參照下圖可以看得出 Agent 就是 Flume 的一個部署實例, 一個完整的 Agent 中包含了三個組件 Source、Channel 和 Sink,Source 是指數據的來源和方式,Channel 是一個數據的緩衝池,Sink 定義了數據輸出的方式和目的地。

Event 是 Flume 定義的一個數據流傳輸的最小單元。Agent 就是一個 Flume 的實例,本質是一個 JVM 進程,該 JVM 進程控制 Event 數據流從外部日誌生產者那裏傳輸到目的地(或者是下一個 Agent)。

Source 消耗由外部(如 Web 服務器)傳遞給它的 Event 。外部以 Flume Source 識別的格式向 Flume 發送 Event 。例如,Avro Flume Source 可接收從 Avro 客戶端(或其他 FlumeSink)接收 Avro Event。

用 Thrift Flume Source 也可以實現類似的流程,接收的 Event 數據可以是任何語言編寫的,只要符合 Thrift 協議即可。

當 Source 接收 Event 時,它將其存儲到一個或多個 channel。該 channel 是一個被動存儲器,可以保持 Event 直到它被 Sink 消耗。『文件 channel』就是一個例子 - 它由本地文件系統支持。

sink 從 channel 中移除 Event 並將其放入外部存儲庫(如 HDFS,通過 Flume 的 HDFS Sink 實現)或將其轉發到流中下一個 Flume Agent(下一跳)的 Flume Source。

Agent 中的 source 和 sink 與 channel 存取 Event 是異步的。

Flume 的 Source 負責消費外部傳遞給它的數據(比如 web 服務器的日誌)。外部的數據生產方以 Flume Source 識別的格式向 Flume 發送 Event。

Flume 可以設置多級 Agent 連接的方式傳輸 Event 數據。也支持扇入和扇出的部署方式,類似於負載均衡方式或多點同時備份的方式

你可以根據自己的業務需求來任意組合傳輸日誌的 Agent 實例,上面這張圖就是一個扇入方式的 Flume 部署方式,前三個 Agent 的數據都彙總到一個 Agent4 上,最後由 Agent4 統一存儲到 HDFS。

Flume Source

Source 的種類非常多,可以適應各種日誌數據源需求。

Spooling Directory Source

Spooling Directory Source 用來蒐集服務器日誌文件夾裏面的日誌,是應用最爲廣泛的 Source。

這個 Source 允許你把要收集的文件放入磁盤上的某個指定目錄。它會將監視這個目錄中產生的新文件,並在新文件出現時從新文件中解析數據出來。數據解析邏輯是可配置的。

在新文件被完全讀入 Channel 之後會重命名該文件以示完成(也可以配置成讀完後立即刪除)。

與 Exec Source 不同,Spooling Directory Source 是可靠的,即使 Flume 重新啓動或被 kill,也不會丟失數據。同時作爲這種可靠性的代價,指定目錄中的文件必須是不可變的、唯一命名的。

Flume 會自動檢測避免這種情況發生,如果發現問題,則會拋出異常:

如果文件在寫入完成後又被再次寫入新內容,Flume 將向其日誌文件(這是指 Flume 自己 logs 目錄下的日誌文件)打印錯誤並停止處理。
如果在以後重新使用以前的文件名,Flume 將向其日誌文件打印錯誤並停止處理。
爲了避免上述問題,生成新文件的時候文件名加上時間戳是個不錯的辦法。

儘管有這個 Source 的可靠性保證,但是仍然存在這樣的情況,某些下游故障發生時會出現重複 Event 的情況。這與其他 Flume 組件提供的保證是一致的。

a1.channels = ch-1
a1.sources = src-1
a1.sources.src-1.type = spooldir
a1.sources.src-1.channels = ch-1
a1.sources.src-1.spoolDir = /var/log/apache/flumeSpool
a1.sources.src-1.fileHeader = true
上面是一個 Spooling Directory Source 配置樣例。Spooling Directory Source 監控了”/var/log/apache/flumeSpool“日誌文件夾。日誌文件夾新增的日誌數據會被當做放到”ch-1“的 channel 中。

Avro Source

Avro Source 監聽 Avro 端口接收從外部 Avro 客戶端發送來的數據流。如果與上一層 Agent 的 Avro Sink 配合使用就組成了一個分層的拓撲結構。

配置範例:

a1.sources = r1
a1.channels = c1
a1.sources.r1.type = avro
a1.sources.r1.channels = c1
a1.sources.r1.bind = 0.0.0.0
a1.sources.r1.port = 4141
Flume channel

channel 是在 Agent 上暫存 Event 的緩衝池。 Event 由 source 添加,由 sink 消費後刪除。

Memory Channel

內存 channel 是把 Event 隊列存儲到內存上,隊列的最大數量就是 capacity 的設定值。它非常適合對吞吐量有較高要求的場景,但也是有代價的,當發生故障的時候會丟失當時內存中的所有 Event。

配置範例:

a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.channels.c1.byteCapacityBufferPercentage = 20
a1.channels.c1.byteCapacity = 800000
Kafka Channel

將 Event 存儲到 Kafka 集羣(必須單獨安裝)。Kafka 提供了高可用性和複製機制,因此如果 Flume 實例或者 Kafka 的實例掛掉,能保證 Event 數據隨時可用。 Kafka channel 可以用於多種場景:

與 source 和 sink 一起:給所有 Event 提供一個可靠、高可用的 channel。
與 source、interceptor 一起,但是沒有 sink:可以把所有 Event 寫入到 Kafka 的 topic 中,來給其他的應用使用。
與 sink 一起,但是沒有 source:提供了一種低延遲、容錯高的方式將 Event 發送的各種 Sink 上,比如:HDFS、HBase、Solr。
由於依賴於該版本附帶的 Kafka 客戶端,Flume1.8 需要 Kafka 0.9 或更高版本。 與之前的 Flume 版本相比,channel 的配置發生了一些變化。
配置參數組織如下:

通常與 channel 相關的配置值應用於 channel 配置級別,比如:a1.channel.k1.type =
與 Kafka 相關的配置值或 Channel 運行的以 “kafka.” 爲前綴(這與 CommonClient Configs 類似),例如:a1.channels.k1.kafka.topic 和 a1.channels.k1.kafka.bootstrap.servers。 這與 hdfs sink 的運行方式沒有什麼不同
特定於生產者 / 消費者的屬性以 kafka.producer 或 kafka.consumer 爲前綴
可能的話,使用 Kafka 的參數名稱,例如:bootstrap.servers 和 acks
當前 Flume 版本是向下兼容的,但是第二個表中列出了一些不推薦使用的屬性,並且當它們出現在配置文件中時,會在啓動時打印警告日誌。
配置範例:

a1.channels.channel1.type = org.apache.flume.channel.kafka.KafkaChannel
a1.channels.channel1.kafka.bootstrap.servers = kafka-1:9092,kafka-2:9092,kafka-3:9092
a1.channels.channel1.kafka.topic = channel1
a1.channels.channel1.kafka.consumer.group.id = flume-consumer
File Channel

文件 channel 使用默認的用戶主目錄內的檢查點和數據目錄的路徑(說的就是上面的 checkpointDir 參數的默認值)。 如果一個 Agent 中有多個活動的文件 channel 實例,而且都是用了默認的檢查點文件, 則只有一個實例可以鎖定目錄並導致其他 channel 初始化失敗。 因此,這時候有必要爲所有已配置的 channel 顯式配置不同的檢查點文件目錄,最好是在不同的磁盤上。 此外,由於文件 channel 將在每次提交後會同步到磁盤,因此將其與將 Event 一起批處理的 sink/source 耦合可能是必要的,以便在多個磁盤不可用於檢查點和數據目錄時提供良好的性能。

配置範例:

a1.channels = c1
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /mnt/flume/checkpoint
a1.channels.c1.dataDirs = /mnt/flume/data
Flume sink

Kafka Sink

這個 Sink 可以把數據發送到 Kafka topic 上。目的就是將 Flume 與 Kafka 集成,以便系統可以處理來自各種 Flume Source 的數據。目前支持 Kafka 0.9.x 發行版。

Flume1.8 不再支持 Kafka 0.9.x(不包括 0.9.x)以前的版本。

Kafka Sink 使用 Event header 中的 topic 和其他關鍵屬性將 Event 發送到 Kafka。 如果 header 中存在 topic,則會將 Event 發送到該特定 topic,從而覆蓋爲 Sink 配置的 topic。 如果 header 中存在指定分區相關的參數,則 Kafka 將使用相關參數發送到指定分區。 header 中特定參數相同的 Event 將被髮送到同一分區。 如果爲空,則將 Event 會被髮送到隨機分區。

Kafka Sink 還提供了 key.deserializer(org.apache.kafka.common.serialization.StringSerializer) 和 value.deserializer(org.apache.kafka.common.serialization.ByteArraySerializer)的默認值,不建議修改這些參數。

a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.topic = mytopic
a1.sinks.k1.kafka.bootstrap.servers = localhost:9092
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1
a1.sinks.k1.kafka.producer.compression.type = snappy
上面給出 Kafka Sink 的配置示例。Kafka 生產者的屬性都是以 kafka.producer 爲前綴。

Kafka 生產者的屬性不限於下面示例的幾個。此外,可以在此處包含您的自定義屬性,並通過作爲方法參數傳入的 Flume Context 對象在預處理器中訪問它們。

HDFS Sink

這個 Sink 將 Event 寫入 Hadoop 分佈式文件系統(也就是 HDFS)。 目前支持創建文本和序列文件。 它支持兩種文件類型的壓縮。 可以根據寫入的時間、文件大小或 Event 數量定期滾動文件(關閉當前文件並創建新文件)。 它還可以根據 Event 自帶的時間戳或系統時間等屬性對數據進行分區。

存儲文件的 HDFS 目錄路徑可以使用格式轉義符,會由 HDFS Sink 進行動態地替換,以生成用於存儲 Event 的目錄或文件名。 使用此 Sink 需要安裝 hadoop, 以便 Flume 可以使用 Hadoop 的客戶端與 HDFS 集羣進行通信。

配置範例:

a1.channels = c1
a1.sinks = k1
a1.sinks.k1.type = hdfs
a1.sinks.k1.channel = c1
a1.sinks.k1.hdfs.path = /flume/events/%y-%m-%d/%H%M/%S
a1.sinks.k1.hdfs.filePrefix = events-
a1.sinks.k1.hdfs.round = true
a1.sinks.k1.hdfs.roundValue = 10
a1.sinks.k1.hdfs.roundUnit = minute
上面的例子中時間戳會向前一個整 10 分鐘取整。比如,一個 Event 的 header 中帶的時間戳是 11:54:34 AM, June 12, 2012,它會保存的 HDFS 路徑就是 /flume/events/2012-06-12/1150/00。

總結

在數據蒐集的時候通常需要處理兩類數據:存儲在關係型數據庫中的結構化數據、散落在服務器日誌文件夾中的日誌數據。日誌數據分散在數以萬計的文件夾中,需要一個高效的日誌數據蒐集工具。

Apache Flume 是一個分佈式、高可靠、高可用的日誌數據蒐集工具。通過配置 Source、Channel、Sink 的方式實現靈活的日誌蒐集。

20 Kylin:以空間換時間

學習要注意到細處,不是粗枝大葉的,這樣可以逐步學習、摸索,找到客觀規律。
—— 徐特立

大家好,這一節我們學習一下 OLAP 分析中,使用非常廣泛的分析引擎:kylin。

OLAP VS OLTP

在學習 Kylin 之前我們先了解一下什麼是 OLAP。OLAP 是 OnLine Analytical Processing 的簡稱。Online 一般指查詢延遲在秒級或毫秒級,可以實現交互式查詢。

OLAP 的查詢一般需要 Scan 大量數據,大多時候只訪問部分列,聚合的需求(Sum,Count,Max,Min 等)會多於明細的需求(查詢原始的明細數據)。

OLAP 的典型查詢一般像:現在各種應用在年末會發布的大數據分析和統計應用,比如 2017 豆瓣讀書報告,2017 豆瓣讀書榜單,網易雲音樂 2017 聽歌報告。

OLAP 在企業中的一個重要應用就是 BI 分析,比如 2017 年最暢銷的手機品牌 Top5。哪類人羣最喜歡小米或華爲手機等等。

OLAP 的特點:

專門用來做決策支持
歷史的,總結的,統一的數據比具體的,獨立的數據更重要
側重於查詢
查詢吞吐量和相應時間是關鍵性能指標
與 OLAP 相對的是 OLTP。OLTP 是 Online Transaction Processing 的簡稱。Transaction 是指形成一個邏輯單元,不可分割的一組讀 & 寫操作。

OLTP 的查詢一般只會訪問少量的記錄,且大多時候都會利用索引。在線的面向終端用戶直接使用的 Web 應用:金融,博客,評論,電商等系統的查詢都是 OLTP 查詢,比如最常見的基於主鍵的 CRUD 操作。

OLTP 的特點:

專門用來做日常的,基本的操作
任務由短的,原子的,隔離的事務組成
處理的數據量在 G 級別
重視一致性和可恢復性
事務的吞吐量是關鍵性能指標
最小化併發衝突
OLTP 需要解決數據的增、刪、改、查的問題,OLAP 需要解決數據聚合的問題。

ROLAP VS MOLAP

OLAP 有多種實現方法,根據存儲數據的方式不同可以分爲 ROLAP、MOLAP。

ROLAP 主要通過數據引擎強大的計算能力,瞬間聚合數據得到 OLAP 結果。MOLAP 則是提前計算聚合好數據模型,查詢的時候只需要返回已經聚合好的數據結果就行了。

ROLAP 需要強大的關係型 DB 引擎支撐,長期以來,由於傳統關係型 DBMS 的數據處理能力有限,所以 ROLAP 模式受到很大的侷限性。隨着分佈式、並行化技術成熟應用,MPP 引擎逐漸表現出強大的高吞吐低時延計算能力,號稱億級秒開的引擎不在少數,ROLAP 模式可以得到更好的延伸。

單從業務實際應用考慮,性能在千萬量級關聯查詢現場計算秒開的情況下,已經可以覆蓋很多應用場景,具備應用的可能性。

MOLAP 表示基於多維數據組織的 OLAP 實現(Multidimensional OLAP)。以多維數據組織方式爲核心,也就是說,MOLAP 使用多維數組存儲數據。

多維數據在存儲中將形成 “立方塊(Cube)“的結構,在 MOLAP 中對” 立方塊 “的” 旋轉”、“切塊”、“切片” 是產生多維數據報表的主要技術。特點是將細節數據和聚合後的數據均保存在 cube 中,所以以空間換效率,查詢時效率高,但生成 cube 時需要大量的時間和空間。

What is KyLin

在開源 MOLAP 方案中,Apache Kylin 應用最爲廣泛,社區活躍度最高的解決方案。

Kylin 的核心思想是預計算,利用空間換時間來加速查詢模式固定的 OLAP 查詢。

Kylin 的理論基礎是 Cube 理論,每一種維度組合稱之爲 Cuboid,所有 Cuboid 的集合是 Cube。 其中由所有維度組成的 Cuboid 稱爲 Base Cuboid,圖中 (A,B,C,D) 即爲 Base Cuboid,所有的 Cuboid 都可以基於 Base Cuboid 計算出來。 在查詢時,Kylin 會自動選擇滿足條件的最 “小” Cuboid,比如下面的 SQL 就會對應 Cuboid(A,B):

select xx from table where A=xx group by B

Kylin 架構

Kylin 自身的組件只有兩個:JobServer 和 QueryServer 。 Kylin 的 JobServer 主要負責將數據源(Hive,Kafka)的數據通過計算引擎(MapReduce,Spark)生成 Cube 存儲到存儲引擎(HBase)中。

QueryServer 主要負責 SQL 的解析,邏輯計劃的生成和優化,向 HBase 的多個 Region 發起請求,並對多個 Region 的結果進行彙總,生成最終的結果集。

在架構設計上,Kylin 的數據源,構建 Cube 的計算引擎,存儲引擎都是可插拔的。Kylin 的核心就是這套可插拔架構,Cube 數據模型和 Cuboid 的算法。

Kylin 基於對 Cube 進行預計算來滿足低延遲的查詢處理。Kylin 的數據源就是 Hive 上的表,以星型模式存在。Kylin 的離線計算過程會讀取 Hive 中的原始數據,將事實表和維表 join 起來,然後對各種維度組合(cuboid)進行計算。計算後的 cube 以 key-value 的形式存儲在 HBase 中。

對於 SQL 請求,Kylin 的查詢引擎會判斷 SQL 能否由 HBase 中的 cube 滿足。如果可以,查詢就會轉換爲 HBase 的 range scan 操作,直接獲得結果數據,因此能做到秒級的響應。

對於無法由 cube 實現的查詢,Kylin 可以將查詢路由給 Hive 執行,不過當前版本由於性能原因,disable 了路由查詢的功能。

Kylin 數據模型

Kylin 將表中的列分爲維度列和指標列。在數據導入和查詢時相同維度列中的指標會按照對應的聚合函數 (Sum, Count, Min, Max, 精確去重,近似去重,百分位數,TOPN) 進行聚合。

圖片描述
在存儲到 HBase 時,Cuboid + 維度 會作爲 HBase 的 Rowkey , 指標會作爲 HBase 的 Value,一般所有指標會在 HBase 的一個列族,每列對應一個指標,但對於較大的去重指標會單獨拆分到第 2 個列族。

Kylin 的數據模型的優化主要是根據實際業務場景,配置合適的 Cube 模型來實現的。影響 Cube 性能的配置主要有以下這些。

Aggregation Groups: Cube 中的維度可以劃分到多個聚合組中。默認 kylin 會把所有維度放在一個聚合組,如果你很好的瞭解你的查詢模式,那麼你可以創建多個聚合組。
Mandatory Dimensions: 必要維度,用於總是出現的維度。
Hierarchy Dimensions: 層級維度,例如 “國家” -> “省” -> “市” 是一個層級。
Joint Dimensions: 聯合維度,有些維度往往一起出現,或者它們的基數非常接近(有 1:1 映射關係)。

Rowkeys: 是由維度編碼值組成。”Dictionary” (字典)是默認的編碼方式;字典只能處理中低基數(少於一千萬)的維度。如果維度基數很高(如大於 1 千萬), 選擇 “false” 然後爲維度輸入合適的長度,通常是那列的最大長度值;如果超過最大值,會被截斷。
Mandatory Cuboids: 維度組合白名單。確保你想要構建的 cuboid 能被構建。

Advanced Dictionaries: “Global Dictionary” 是用於精確計算 COUNT DISTINCT 的字典,它會將一個非 integer 的值轉成 integer,以便於 bitmap 進行去重。

“Segment Dictionary” 是另一個用於精確計算 COUNT DISTINCT 的字典,與 Global Dictionary 不同的是,它是基於一個 segment 的值構建的,因此不支持跨 segments 的彙總計算。

Advanced ColumnFamily: 如果有超過一個的 COUNT DISTINCT 或 TopN 度量,你可以將它們放在更多列簇中,以優化與 HBase 的 I/O。

總結

OLAP 和 OLTP 分別解決了數據聚合問題、數據增刪改查問題。OLAP 又分爲 ROLAP 和 MOLAP 兩種。

ROLAP 藉助關係型數據庫強大的計算能力,現計算數據聚合結果。MOLAP 則是提前預計算數據聚合結果。

MOLAP 的裏面開源數據庫的代表是 Apache kylin。Apache kylin 通過空間換時間的方式實現了極致的 OLAP 性能,我們可以根據實際的業務場景,適配合適的 Cube 模型來優化 Apache kylin 的查詢性能。

21 Doris:MPP架構的ROLAP王者

要成就一件大事業,必須從小事做起。
——列寧

上一小節我們學習 MOLAP 的代表 Kylin。這一小節我們學習一下 ROLAP 代表:Doris。

What is Doris

Doris 是一個 MPP 的 OLAP 系統,主要整合了 Google Mesa(數據模型),Apache Impala(MPP Query Engine) 和 Apache ORCFile (存儲格式,編碼和壓縮) 的技術。

Doris 的系統架構如下,主要分爲 FE 和 BE 兩個組件。

FE:Frontend,即 Doris 的前端節點。主要負責接收和返回客戶端請求、元數據以及集羣管理、查詢計劃生成等工作。
BE:Backend,即 Doris 的後端節點。主要負責數據存儲與管理、查詢計劃執行等工作。
圖片描述
如上圖,Doris 的整體架構分爲兩層。多個 FE 組成第一層,提供 FE 的橫向擴展和高可用。多個 BE 組成第二層,負責數據存儲與管理。本文主要介紹 FE 這一層中,元數據的設計與實現方式。

FE 節點分爲 follower 和 observer 兩類。各個 FE 之間,通過 bdbje(BerkeleyDB Java Edition)進行 leader 選舉,數據同步等工作。
follower 節點通過選舉,其中一個 follower 成爲 leader 節點,負責元數據的寫入操作。當 leader 節點宕機後,其他 follower 節點會重新選舉出一個 leader,保證服務的高可用。
observer 節點僅從 leader 節點進行元數據同步,不參與選舉。可以橫向擴展以提供元數據的讀服務的擴展性。
Doris 數據模型

在 Doris 中,數據以表(Table)的形式進行邏輯上的描述。一張表包括行(Row)和列(Column)。Row 即用戶的一行數據。Column 用於描述一行數據中不同的字段。

Column 可以分爲兩大類:Key 和 Value。從業務角度看,Key 和 Value 可以分別對應維度列和指標列。

根據是否對 value 預聚合、key 是否是唯一的將 Doris 數據模型分爲:Aggregate、Uniq、Duplicate。

聚合模型

Uniq 可以看做是一種特殊的 Aggregate。Aggregate、Uniq 都屬於聚合模型。

假設業務有如下數據表模式:

ColumnName Type AggregationType Comment
user_id LARGEINT 用戶 id
date DATE 數據灌入日期
city VARCHAR(20) 用戶所在城市
age SMALLINT 用戶年齡
sex TINYINT 用戶性別
last_visit_date DATETIME REPLACE 用戶最後一次訪問時間
cost BIGINT SUM 用戶總消費
max_dwell_time INT MAX 用戶最大停留時間
min_dwell_time INT MIN 用戶最小停留時間
如果轉換成 Aggregate 模型建表語句則如下(省略建表語句中的 Partition 和 Distribution 信息)

CREATE TABLE IF NOT EXISTS example_db.expamle_tbl
(
user_id LARGEINT NOT NULL COMMENT “用戶id”,
date DATE NOT NULL COMMENT “數據灌入日期時間”,
city VARCHAR(20) COMMENT “用戶所在城市”,
age SMALLINT COMMENT “用戶年齡”,
sex TINYINT COMMENT “用戶性別”,
last_visit_date DATETIME REPLACE DEFAULT “1970-01-01 00:00:00” COMMENT “用戶最後一次訪問時間”,
cost BIGINT SUM DEFAULT “0” COMMENT “用戶總消費”,
max_dwell_time INT MAX DEFAULT “0” COMMENT “用戶最大停留時間”,
min_dwell_time INT MIN DEFAULT “99999” COMMENT “用戶最小停留時間”,
)
AGGREGATE KEY(user_id, date, timestamp, city, age, sex)
… /* 省略 Partition 和 Distribution 信息 */

可以看到,這是一個典型的用戶信息和訪問行爲的事實表。在一般星型模型中,用戶信息和訪問行爲一般分別存放在維度表和事實表中。這裏我們爲了更加方便的解釋 Doris 的數據模型,將兩部分信息統一存放在一張表中。

表中的列按照是否設置了 AggregationType,分爲 Key (維度列) 和 Value(指標列)。沒有設置 AggregationType 的,如 user_id、date、age … 等稱爲 Key,而設置了 AggregationType 的稱爲 Value。

當我們導入數據時,對於 Key 列相同的行會聚合成一行,而 Value 列會按照設置的 AggregationType 進行聚合。 AggregationType 目前有以下四種聚合方式:

SUM:求和,多行的 Value 進行累加。
REPLACE:替代,下一批數據中的 Value 會替換之前導入過的行中的 Value。
MAX:保留最大值。
MIN:保留最小值。
如果 AggregationType 都爲 REPLACE 時候,Aggregate 模型就變成了 Uniq 模型。

Doris 中比較獨特的聚合函數是 Replace 函數,這個聚合函數能夠保證相同 Keys 的記錄只保留最新的 Value,可以藉助這個 Replace 函數來實現 點更新。

Uniq 模型模型下,主鍵是唯一的,如果新導入的數據跟表裏面已有數據有相同的主鍵,新導入的數據會替換表裏面已有的數據。

明細模型

在很多分析場景下,我需要分析明細數據,這時候我們需要用到明細模型。

在 Doris 裏面 Duplicate 模型是一種明細模型。

舉例說明。

ColumnName Type SortKey Comment
timestamp DATETIME Yes 日誌時間
type INT Yes 日誌類型
error_code INT Yes 錯誤碼
error_msg VARCHAR(1024) No 錯誤詳細信息
op_id BIGINT No 負責人 id
op_time DATETIME No 處理時間
建表語句如下:

CREATE TABLE IF NOT EXISTS example_db.expamle_tbl
(
timestamp DATETIME NOT NULL COMMENT “日誌時間”,
type INT NOT NULL COMMENT “日誌類型”,
error_code INT COMMENT “錯誤碼”,
error_msg VARCHAR(1024) COMMENT “錯誤詳細信息”,
op_id BIGINT COMMENT “負責人id”,
op_time DATETIME COMMENT “處理時間”
)
DUPLICATE KEY(timestamp, type)
… /* 省略 Partition 和 Distribution 信息 */

這種數據模型區別於 Aggregate 和 Uniq 模型。數據完全按照導入文件中的數據進行存儲,不會有任何聚合。即使兩行數據完全相同,也都會保留。 而在建表語句中指定的 DUPLICATE KEY,只是用來指明底層數據按照那些列進行排序。

Doris Rollup

Doris 中和 Kylin 的 Cuboid 等價的概念是 RollUp 表,**Cuboid 和 RollUp 表都可以認爲是一種 Materialized Views 或者 Index **。Doris 的 RollUp 表和 Kylin 的 Cuboid 一樣,在查詢時不需要顯示指定,系統內部會根據查詢條件進行路由。

Doris 中 RollUp 表的路由規則如下:

選擇包含所有查詢列的 RollUp 表
按照過濾和排序的 Column 篩選最符合的 RollUp 表
按照 Join 的 Column 篩選最符合的 RollUp 表
行數最小的
列數最小的
爲了大家更好的理解 Doris 的 Rollup,這裏將 Rollup 和 Cuboid 進行對比。

Doris 元數據結構

Doris 的元數據是全內存的。每個 FE 內存中,都維護一個完整的元數據鏡像。

元數據在內存中整體採用樹狀的層級結構存儲,並且通過添加輔助結構,能夠快速訪問各個層級的元數據信息。

Doris 元信息所存儲的內容:
有哪些“

Doris 的元數據主要存儲 4 類數據:

用戶數據信息。包括數據庫、表的 Schema、分片信息等。
各類作業信息。如導入作業,Clone 作業、SchemaChange 作業等。
用戶及權限信息。
集羣及節點信息。
Doris 數據流

元數據的數據流具體過程如下:

只有 leader FE 可以對元數據進行寫操作。寫操作在修改 leader 的內存後,會序列化爲一條 log,按照 key-value 的形式寫入 bdbje。其中 key 爲連續的整型,作爲 log id,value 即爲序列化後的操作日誌。

日誌寫入 bdbje 後,bdbje 會根據策略(寫多數 / 全寫),將日誌複製到其他 non-leader 的 FE 節點。non-leader FE 節點通過對日誌回放,修改自身的元數據內存鏡像,完成與 leader 節點的元數據同步。

leader 節點的日誌條數達到閾值後(默認 10w 條),會啓動 checkpoint 線程。checkpoint 會讀取已有的 image 文件,和其之後的日誌,重新在內存中回放出一份新的元數據鏡像副本。

然後將該副本寫入到磁盤,形成一個新的 image。之所以是重新生成一份鏡像副本,而不是將已有鏡像寫成 image,主要是考慮寫 image 加讀鎖期間,會阻塞寫操作。所以每次 checkpoint 會佔用雙倍內存空間。

image 文件生成後,leader 節點會通知其他 non-leader 節點新的 image 已生成。non-leader 主動通過 http 拉取最新的 image 文件,來更換本地的舊文件。

bdbje 中的日誌,在 image 做完後,會定期刪除舊的日誌。

總結

Doris 是從百度內部自主研發並貢獻到 Apache 開源社區的 ROLAP 數據庫。

Doris 整合了 Google Mesa(數據模型),Apache Impala(MPP Query Engine) 和 Apache ORCFile (存儲格式,編碼和壓縮) 技術,在數據查詢延遲上表現非常突出。

Doris 的聚合模型主要用於數據的彙總分析,明細模型主要用於明細數據的查詢。相對於 Kylin 只支持彙總模型,Doris 適用的數據場景更加廣泛。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章