深入理解大資料架構之——Lambda架構
目錄
- 傳統系統的問題
- Lambda架構簡介
- Lambda架構關鍵特性
- 資料系統的本質
- Lambda的三層架構
- Lambda架構元件選型
- 總結
原文連結:https://jiang-hao.com/articles/2019/big-data-lambda-architecture.html
傳統系統的問題
“我們正在從IT時代走向DT時代(資料時代)。IT和DT之間,不僅僅是技術的變革,更是思想意識的變革,IT主要是為自我服務,用來更好地自我控制和管理,DT則是啟用生產力,讓別人活得比你好”
——阿里巴巴董事局主席馬雲。
資料量從M的級別到G的級別到現在T的級、P的級別。資料量的變化資料管理系統(DBMS)和數倉系統(DW)也在悄然的變化著。 傳統應用的資料系統架構設計時,應用直接訪問資料庫系統。當用戶訪問量增加時,資料庫無法支撐日益增長的使用者請求的負載時,從而導致資料庫伺服器無法及時響應使用者請求,出現超時的錯誤。出現這種情況以後,在系統架構上就採用下圖的架構,在資料庫和應用中間過一層緩衝隔離,緩解資料庫的讀寫壓力。
然而,當用戶訪問量持續增加時,就需要考慮讀寫分離技術(Master-Slave)架構則如下圖,分庫分表技術。現在,架構變得越來越複雜了,增加佇列、分割槽、複製等處理邏輯。應用程式需要了解資料庫的schema,才能訪問到正確的資料。
商業現實已經發生了變化,所以現在更快做出的決定更有價值。除此之外,技術也在不斷髮展。Kafka,Storm,Trident,Samza,Spark,Flink,Parquet,Avro,Cloud providers等都是工程師和企業廣泛採用的流行語。因此,現代基於Hadoop的M/R管道(使用Kafka,Avro和資料倉庫等現代二進位制格式,即Amazon Redshift,用於臨時查詢)可能採用以下方式:
這看起來相當不錯,但它仍然是一種傳統的批處理方式,具有所有已知的缺點,主要原因是客戶端的資料在批處理花費大量時間完成之前的資料處理時,新的資料已經進入而導致資料過時。
Lambda架構簡介
對低成本規模化的需求促使人們開始使用分散式檔案系統,例如 HDFS和基於批量資料的計算系統(MapReduce 作業)。但是這種系統很難做到低延遲。用 Storm 開發的實時流處理技術可以幫助解決延遲性的問題,但並不完美。其中的一個原因是,Storm 不支援 exactly-once 語義,因此不能保證狀態資料的正確性,另外它也不支援基於事件時間的處理。有以上需求的使用者不得不在自己的應用程式程式碼中加入這些功能。後來出現了一種混合分析的方法,它將上述兩個方案結合起來,既保證低延遲,又保障正確性。這個方法被稱作 Lambda 架構,它通過批量 MapReduce作業提供了雖有些延遲但是結果準確的計算,同時通過Storm將最新資料的計算結果初步展示出來。
Lambda架構是由Storm的作者Nathan Marz提出的一個實時大資料處理框架。Marz在Twitter工作期間開發了著名的實時大資料處理框架Storm,Lambda架構是其根據多年進行分散式大資料系統的經驗總結提煉而成。Lambda架構的目標是設計出一個能滿足實時大資料系統關鍵特性的架構,包括有:高容錯、低延時和可擴充套件等。Lambda架構整合離線計算和實時計算,融合不可變性(Immunability),讀寫分離和複雜性隔離等一系列架構原則,可整合Hadoop,Kafka,Storm,Spark,Hbase等各類大資料元件。
Lambda架構關鍵特性
Marz認為大資料系統應具有以下的關鍵特性:
- Robust and fault-tolerant(容錯性和魯棒性):對大規模分散式系統來說,機器是不可靠的,可能會當機,但是系統需要是健壯、行為正確的,即使是遇到機器錯誤。除了機器錯誤,人更可能會犯錯誤。在軟體開發中難免會有一些Bug,系統必須對有Bug的程式寫入的錯誤資料有足夠的適應能力,所以比機器容錯性更加重要的容錯性是人為操作容錯性。對於大規模的分散式系統來說,人和機器的錯誤每天都可能會發生,如何應對人和機器的錯誤,讓系統能夠從錯誤中快速恢復尤其重要。
- Low latency reads and updates(低延時):很多應用對於讀和寫操作的延時要求非常高,要求對更新和查詢的響應是低延時的。
- Scalable(橫向擴容):當資料量/負載增大時,可擴充套件性的系統通過增加更多的機器資源來維持效能。也就是常說的系統需要線性可擴充套件,通常採用scale out(通過增加機器的個數)而不是scale up(通過增強機器的效能)。
- General(通用性):系統需要能夠適應廣泛的應用,包括金融領域、社交網路、電子商務資料分析等。
- Extensible(可擴充套件):需要增加新功能、新特性時,可擴充套件的系統能以最小的開發代價來增加新功能。
- Allows ad hoc queries(方便查詢):資料中蘊含有價值,需要能夠方便、快速的查詢出所需要的資料。
- Minimal maintenance(易於維護):系統要想做到易於維護,其關鍵是控制其複雜性,越是複雜的系統越容易出錯、越難維護。
- Debuggable(易除錯):當出問題時,系統需要有足夠的資訊來除錯錯誤,找到問題的根源。其關鍵是能夠追根溯源到每個資料生成點。
資料系統的本質
為了設計出能滿足前述的大資料關鍵特性的系統,我們需要對資料系統有本質性的理解。我們可將資料系統簡化為:
資料系統 = 資料 + 查詢
從而從資料和查詢兩方面來認識大資料系統的本質。
資料的特性: when & what
我們先從“資料”的特性談起。資料是一個不可分割的單位,資料有兩個關鍵的性質:When和What。
- When是指資料是與時間相關的,資料一定是在某個時間點產生的。比如Log日誌就隱含著按照時間先後順序產生的資料,Log前面的日誌資料一定先於Log後面的日誌資料產生;訊息系統中訊息的接受者一定是在訊息的傳送者傳送訊息後接收到的訊息。相比於資料庫,資料庫中表的記錄就丟失了時間先後順序的資訊,中間某條記錄可能是在最後一條記錄產生後發生更新的。對於分散式系統,資料的時間特性尤其重要。分散式系統中資料可能產生於不同的系統中,時間決定了資料發生的全域性先後順序。比如對一個值做算術運算,先+2,後3,與先3,後+2,得到的結果完全不同。資料的時間性質決定了資料的全域性發生先後,也就決定了資料的結果。
- What是指資料的本身。由於資料跟某個時間點相關,所以資料的本身是不可變的(immutable),過往的資料已經成為事實(Fact),你不可能回到過去的某個時間點去改變資料事實。這也就意味著對資料的操作其實只有兩種:讀取已存在的資料和新增更多的新資料。採用資料庫的記法,CRUD就變成了CR,Update和Delete本質上其實是新產生的資料資訊,用C來記錄。
資料的儲存:Store Everything Rawly and Immutably
根據上述對資料本質特性的分析,Lamba架構中對資料的儲存採用的方式是:資料不可變,儲存所有資料。
通過採用不可變方式儲存所有的資料,可以有如下好處:
- 簡單。採用不可變的資料模型,儲存資料時只需要簡單的往主資料集後追加資料即可。相比於採用可變的資料模型,為了Update操作,資料通常需要被索引,從而能快速找到要更新的資料去做更新操作。
- 應對人為和機器的錯誤。前述中提到人和機器每天都可能會出錯,如何應對人和機器的錯誤,讓系統能夠從錯誤中快速恢復極其重要。不可變性(Immutability)和重新計算(Recomputation)則是應對人為和機器錯誤的常用方法。採用可變資料模型,引發錯誤的資料有可能被覆蓋而丟失。相比於採用不可變的資料模型,因為所有的資料都在,引發錯誤的資料也在。修復的方法就可以簡單的是遍歷資料集上儲存的所有的資料,丟棄錯誤的資料,重新計算得到Views。重新計算的關鍵點在於利用資料的時間特性決定的全域性次序,依次順序重新執行,必然能得到正確的結果。
當前業界有很多采用不可變資料模型來儲存所有資料的例子。比如分散式資料庫Datomic,基於不可變資料模型來儲存資料,從而簡化了設計。分散式訊息中介軟體Kafka,基於Log日誌,以追加append-only的方式來儲存訊息。
查詢的本質
查詢是個什麼概念?Marz給查詢如下一個簡單的定義:
Query = Function(All Data)
該等式的含義是:查詢是應用於資料集上的函式。該定義看似簡單,卻幾乎囊括了資料庫和資料系統的所有領域:RDBMS、索引、OLAP、OLTP、MapReduce、EFL、分散式檔案系統、NoSQL等都可以用這個等式來表示。
讓我們進一步深入看一下函式的特性,從而挖掘函式自身的特點來執行查詢。 有一類稱為Monoid特性的函式應用非常廣泛。Monoid的概念來源於範疇學(Category Theory),其一個重要特性是滿足結合律。如整數的加法就滿足Monoid特性:
(a+b)+c=a+(b+c)
不滿足Monoid特性的函式很多時候可以轉化成多個滿足Monoid特性的函式的運算。如多個數的平均值Avg函式,多個平均值沒法直接通過結合來得到最終的平均值,但是可以拆成分母除以分子,分母和分子都是整數的加法,從而滿足Monoid特性。
Monoid的結合律特性在分散式計算中極其重要,滿足Monoid特性意味著我們可以將計算分解到多臺機器並行運算,然後再結合各自的部分運算結果得到最終結果。同時也意味著部分運算結果可以儲存下來被別的運算共享利用(如果該運算也包含相同的部分子運算),從而減少重複運算的工作量。
Lambda的三層架構
有了上面對資料系統本質的探討,下面我們來討論大資料系統的關鍵問題:如何實時地在任意大資料集上進行查詢?大資料再加上實時計算,問題的難度比較大。
最簡單的方法是,根據前述的查詢等式Query = Function(All Data)
,在全體資料集上線上執行查詢函式得到結果。但如果資料量比較大,該方法的計算代價太大了,所以不現實。
Lambda架構通過分解的三層架構來解決該問題:Batch Layer,Speed Layer和Serving Layer。
Batch Layer
理想狀態下,任何資料訪問都可以從表示式Query= function(all data)開始,但是,若資料達到相當大的一個級別(例如PB),且還需要支援實時查詢時,就需要耗費非常龐大的資源。一個解決方式是預運算查詢函式(precomputed query function)。書中將這種預運算查詢函式稱之為Batch View(A),這樣當需要執行查詢時,可以從Batch View中讀取結果。這樣一個預先運算好的View是可以建立索引的,因而可以支援隨機讀取(B)。於是系統就變成:
(A)batch view = function(all data)
(B)query = function(batch view)
在Lambda架構中,實現(A)batch view =function(all data)的部分稱之為Batch Layer。Batch Layer的功能主要有兩點:
- 儲存master dataset, 這是一個不變的持續增長的資料集
- 在master dataset上預先計算查詢函式,構建查詢所對應的View
儲存資料集
根據前述對資料When&What特性的討論,Batch Layer採用不可變模型儲存所有的資料。因為資料量比較大,可以採用HDFS之類的大資料儲存方案。如果需要按照資料產生的時間先後順序存放資料,可以考慮如InfluxDB之類的時間序列資料庫(TSDB)儲存方案。
構建查詢View
上面說到根據等式Query = Function(All Data),在全體資料集上線上執行查詢函式得到結果的代價太大。但如果我們預先在資料集上計算並儲存查詢函式的結果,查詢的時候就可以直接返回結果(或通過簡單的加工運算就可得到結果)而無需重新進行完整費時的計算了。這兒可以把Batch Layer看成是一個數據預處理的過程。我們把針對查詢預先計算並儲存的結果稱為View,View是Lambda架構的一個核心概念,它是針對查詢的優化,通過View即可以快速得到查詢結果。
顯然,batch view是一個批處理過程,如採用Hadoop或spark支援的map-reduce方式。採用這種方式計算得到的每個view都支援再次計算,且每次計算的結果都相同。Batch Layer的工作可以簡單的用如下偽碼錶示:
該工作看似簡單,實質非常強大。任何人為或機器發生的錯誤,都可以通過修正錯誤後重新計算來恢復得到正確結果。
對View的理解
View是一個和業務關聯性比較大的概念,View的建立需要從業務自身的需求出發。一個通用的資料庫查詢系統,查詢對應的函數千變萬化,不可能窮舉。但是如果從業務自身的需求出發,可以發現業務所需要的查詢常常是有限的。Batch Layer需要做的一件重要的工作就是根據業務的需求,考察可能需要的各種查詢,根據查詢定義其在資料集上對應的Views。
Batch Layer的Immutable data模型和Views
如下圖agent id=50023的人,在10:00:06分的時候,狀態是calling,在10:00:10的時候狀態為waiting。在傳統的資料庫設計中,直接後面的紀錄覆蓋前面的紀錄,而在Immutable資料模型中,不會對原有資料進行更改,而是採用插入修改紀錄的形式更改歷史紀錄。
上文所提及的View是上圖中預先計算得到的相關檢視,例如:2016-06-21當天所有上線的agent數,每條熱線、公司下上線的Agent數。根據業務需要,預先計算出結果。此過程相當於傳統數倉建模的應用層,應用層也是根據業務場景,預先加工出的view。
Speed Layer
Batch Layer可以很好的處理離線資料,但有很多場景資料不斷實時生成,並且需要實時查詢處理。Speed Layer正是用來處理增量的實時資料。
Speed Layer和Batch Layer比較類似,對資料進行計算並生成Realtime View,其主要區別在於:
- Speed Layer處理的資料是最近的增量資料流,Batch Layer處理的全體資料集
- Speed Layer為了效率,接收到新資料時不斷更新Realtime View,而Batch Layer根據全體離線資料集直接得到Batch View。Speed Layer是一種增量計算,而非重新計算(recomputation)
- Speed Layer因為採用增量計算,所以延遲小,而Batch Layer是全資料集的計算,耗時比較長
綜上所訴,Speed Layer是Batch Layer在實時性上的一個補充。Speed Layer可總結為:
(C)realtime view=function(realtime view,new data)
注意,realtime view是基於新資料和已有的realtime view。
Lambda架構將資料處理分解為Batch Layer和Speed Layer有如下優點:
- 容錯性。Speed Layer中處理的資料也不斷寫入Batch Layer,當Batch Layer中重新計算的資料集包含Speed Layer處理的資料集後,當前的Realtime View就可以丟棄,這也就意味著Speed Layer處理中引入的錯誤,在Batch Layer重新計算時都可以得到修正。這點也可以看成是CAP理論中的最終一致性(Eventual Consistency)的體現。
- 複雜性隔離。Batch Layer處理的是離線資料,可以很好的掌控。Speed Layer採用增量演算法處理實時資料,複雜性比Batch Layer要高很多。通過分開Batch Layer和Speed Layer,把複雜性隔離到Speed Layer,可以很好的提高整個系統的魯棒性和可靠性。
如前所述,任何傳入查詢都必須通過合併來自批量檢視和實時檢視的結果來得到答案,因此這些檢視需要滿足Monoid的結合律特性。需要注意的一點是,實時檢視是以前的實時檢視和新資料增量的函式,因此可以使用增量演算法。批處理檢視是所有資料的函式,因此應該在那裡使用重算演算法。
Serving Layer
Lambda架構的Serving Layer用於響應使用者的查詢請求,合併Batch View和Realtime View中的結果資料集到最終的資料集。
這兒涉及到資料如何合併的問題。前面我們討論了查詢函式的Monoid性質,如果查詢函式滿足Monoid性質,即滿足結合律,只需要簡單的合併Batch View和Realtime View中的結果資料集即可。否則的話,可以把查詢函式轉換成多個滿足Monoid性質的查詢函式的運算,單獨對每個滿足Monoid性質的查詢函式進行Batch View和Realtime View中的結果資料集合並,然後再計算得到最終的結果資料集。另外也可以根據業務自身的特性,運用業務自身的規則來對Batch View和Realtime View中的結果資料集合並。
綜上所訴,Serving Layer採用如下等式表示:
(D)query=function(batch view, realtime view)
Lambda架構元件選型
上面分別討論了Lambda架構的三層:Batch Layer,Speed Layer和Serving Layer。總結下來,Lambda架構就是如下的三個等式:
batch view = function(all data)
realtime view = function(realtime view, new data)
query = function(batch view, realtime view)
下圖給出了Lambda架構的一個完整檢視和流程。
資料流進入系統後,同時發往Batch Layer和Speed Layer處理。Batch Layer以不可變模型離線儲存所有資料集,通過在全體資料集上不斷重新計算構建查詢所對應的Batch Views。Speed Layer處理增量的實時資料流,不斷更新查詢所對應的Realtime Views。Serving Layer響應使用者的查詢請求,合併Batch View和Realtime View中的結果資料集到最終的資料集。
元件選型
下圖給出了Lambda架構中各元件在大資料生態系統中和阿里集團的常用元件。資料流儲存選用不可變日誌的分散式系統Kafka、TT、Metaq;BatchLayer資料集的儲存選用Hadoop的HDFS或者阿里雲的ODPS;BatchView的加工採用MapReduce;BatchView資料的儲存採用Mysql(查詢少量的最近結果資料)、Hbase(查詢大量的歷史結果資料)。SpeedLayer採用增量資料處理Storm、Flink;RealtimeView增量結果資料集採用記憶體資料庫Redis。
另一個實現版本:
根據batch layer的特點,具備儲存(HDFS)和計算(MapReduce)的Hadoop顯然是第一人選,而batch view 可以是hadoop本身的hdfs 或者基於hdfs的所構建的類似hive那樣的倉庫,speed layer因為時效性的影響,採用實時流式處理系統,例如strom或者spark streaming, 而speed view 可以存在HBase 或者其他類似的Nosql資料庫。server layer 提供使用者查詢的方法,採用facebook 開源的Impala,統一入口查詢。或者自己實現hive和HBase統一查詢。這是兩年前的文章,當時spark 還沒那麼火,現在看來spark可以直接作為batch和speed層的替代者了。
選型原則
Lambda架構是個通用框架,各個層選型時不要侷限時上面給出的元件,特別是對於View的選型。從我對Lambda架構的實踐來看,因為View是個和業務關聯性非常大的概念,View選擇元件時關鍵是要根據業務的需求,來選擇最適合查詢的元件。不同的View元件的選擇要深入挖掘資料和計算自身的特點,從而選擇出最適合資料和計算自身特點的元件,同時不同的View可以選擇不同的元件。
總結
在過去Lambda資料架構成為每一個公司大資料平臺必備的架構,它解決了一個公司大資料批量離線處理和實時資料處理的需求。一個典型的Lambda架構如下:
資料從底層的資料來源開始,經過各種各樣的格式進入大資料平臺,在大資料平臺中經過Kafka、Flume等資料元件進行收集,然後分成兩條線進行計算。一條線是進入流式計算平臺(例如 Storm、Flink或者Spark Streaming),去計算實時的一些指標;另一條線進入批量資料處理離線計算平臺(例如Mapreduce、Hive,Spark SQL),去計算T+1的相關業務指標,這些指標需要隔日才能看見。
Lambda架構經歷多年的發展,其優點是穩定,對於實時計算部分的計算成本可控,批量處理可以用晚上的時間來整體批量計算,這樣把實時計算和離線計算高峰分開,這種架構支撐了資料行業的早期發展,但是它也有一些致命缺點,並在大資料3.0時代越來越不適應資料分析業務的需求。缺點如下:
實時與批量計算結果不一致引起的資料口徑問題:因為批量和實時計算走的是兩個計算框架和計算程式,算出的結果往往不同,經常看到一個數字當天看是一個數據,第二天看昨天的資料反而發生了變化。
批量計算在計算視窗內無法完成:在IOT時代,資料量級越來越大,經常發現夜間只有4、5個小時的時間視窗,已經無法完成白天20多個小時累計的資料,保證早上上班前準時出資料已成為每個大資料團隊頭疼的問題。
開發和維護的複雜性問題:Lambda 架構需要在兩個不同的 API(application programming interface,應用程式程式設計介面)中對同樣的業務邏輯進行兩次程式設計:一次為批量計算的ETL系統,一次為流式計算的Streaming系統。針對同一個業務問題產生了兩個程式碼庫,各有不同的漏洞。這種系統實際上非常難維護
伺服器儲存大:資料倉庫的典型設計,會產生大量的中間結果表,造成資料急速膨脹,加大伺服器儲存壓力。
也就是由於Lambda架構的以上侷限性,Kappa應運而生,它比Lambda架構更加靈活和精簡,具體將另文介紹。
Kappa架構:
原文連結:https://jiang-hao.com/articles/2019/big-data-lambda-architecture.