打造100億SDK覆蓋量的大數據系統,個推從離線到實時演進的三階段 | 36大數據
http://www.36dsj.com/archives/58518
基礎建設階段個推完成幾項工作:采用Lambda架構(Batch Layer、Speed Layer、ServingLayer);引入Hadoop(Hdfs、Hive/MR、Hbase、Mahout等);采用ES、SolrCloud+ HBase方案 實現多維度檢索;引入Flume 、Kafka、Camus和優化改造日志傳輸和引入和優化國產開源的Redis集群方案-Codis 。
大數據系統的Lambda架構 - 推酷
http://www.tuicool.com/articles/uiyYFf
Nathan Marz的大作Big Data: Principles and best practices of scalable real-time data systems介紹了Labmda Architecture的概念,用于在大數據架構中,如何讓real-time與batch job更好地結合起來,以達成對大數據的實時處理。
傳統系統的問題
在傳統數據庫的設計中,無法很好地支持系統的可伸縮性。當用戶訪問量增加時,數據庫無法滿足日益增長的用戶請求負載,從而導致數據庫服務器無法及時響應用戶請求,出現超時錯誤。
解決的辦法是在Web服務器與數據庫之間增加一個異步處理的隊列。如下圖所示:
當Web Server收到頁面請求時,會將消息添加到隊列中。在DB端,創建一個Worker定期從隊列中取出消息進行處理,例如每次讀取100條消息。這相當于在兩者之間建立了一個緩沖。
但是,這一方案并沒有從本質上解決數據庫overload的問題,且當worker無法跟上writer的請求時,就需要增加多個worker并發執行,數據庫又將再次成為響應請求的瓶頸。一個解決辦法是對數據庫進行分區(horizontal partitioning或者sharding)。分區的方式通常以Hash值作為key。這樣就需要應用程序端知道如何去尋找每個key所在的分區。
問題仍然會隨著用戶請求的增加接踵而來。當之前的分區無法滿足負載時,就需要增加更多分區,這時就需要對數據庫進行reshard。resharding的工作非常耗時而痛苦,因為需要協調很多工作,例如數據的遷移、更新客戶端訪問的分區地址,更新應用程序代碼。如果系統本身還提供了在線訪問服務,對運維的要求就更高。稍有不慎,就可能導致數據寫到錯誤的分區,因此必須要編寫腳本來自動完成,且需要充分的測試。
即使分區能夠解決數據庫負載問題,卻還存在容錯性(Fault-Tolerance)的問題。解決辦法:
改變queue/worker的實現。當消息發送給不可用的分區時,將消息放到“pending”隊列,然后每隔一段時間對pending隊列中的消息進行處理。
使用數據庫的replication功能,為每個分區增加slave。
問題并沒有得到完美地解決。假設系統出現問題,例如在應用系統代碼端不小心引入了一個bug,使得對頁面的請求重復提交了一次,這就導致了重復的請求數據。糟糕的是,直到24小時之后才發現了該問題,此時對數據的破壞已經造成了。即使每周的數據備份也無法解決此問題,因為它不知道到底是哪些數據受到了破壞(corrupiton)。由于人為錯誤總是不可避免的,我們在架構時應該如何規避此問題?
現在,架構變得越來越復雜,增加了隊列、分區、復制、重分區腳本(resharding scripts)。應用程序還需要了解數據庫的schema,并能訪問到正確的分區。問題在于:數據庫對于分區是不了解的,無法幫助你應對分區、復制與分布式查詢。最糟糕的問題是系統并沒有為人為錯誤進行工程設計,僅靠備份是不能治本的。歸根結底,系統還需要限制因為人為錯誤導致的破壞。
數據系統的概念
大數據處理技術需要解決這種可伸縮性與復雜性。首先要認識到這種分布式的本質,要很好地處理分區與復制,不會導致錯誤分區引起查詢失敗,而是要將這些邏輯內化到數據庫中。當需要擴展系統時,可以非常方便地增加節點,系統也能夠針對新節點進行rebalance。
其次是要讓數據成為不可變的。原始數據永遠都不能被修改,這樣即使犯了錯誤,寫了錯誤數據,原來好的數據并不會受到破壞。
何謂“數據系統”?Nathan Marz認為:
如果數據系統通過查找過去的數據去回答問題,則通常需要訪問整個數據集。
因此可以給data system的最通用的定義:
Query = function(all data)
接下來,本書作者介紹了Big Data System所需具備的屬性:
健壯性和容錯性(Robustness和Fault Tolerance)
低延遲的讀與更新(Low Latency reads and updates)
可伸縮性(Scalability)
通用性(Generalization)
可擴展性(Extensibility)
內置查詢(Ad hoc queries)
維護最小(Minimal maintenance)
可調試性(Debuggability)
Lambda架構
Lambda架構的主要思想就是將大數據系統構建為多個層次,如下圖所示:
理想狀態下,任何數據訪問都可以從表達式 Query = function(all data)
開始,但是,若數據達到相當大的一個級別(例如PB),且還需要支持實時查詢時,就需要耗費非常龐大的資源。
一個解決方式是預運算查詢函數(precomputed query funciton)。書中將這種預運算查詢函數稱之為 Batch View ,這樣當需要執行查詢時,可以從Batch View中讀取結果。這樣一個預先運算好的View是可以建立索引的,因而可以支持隨機讀取。于是系統就變成:
batch view = function(all data)query = function(batch view)
Batch Layer
在Lambda架構中,實現 batch view = function(all data)
的部分被稱之為 batch layer 。它承擔了兩個職責:
存儲Master Dataset,這是一個不變的持續增長的數據集
針對這個Master Dataset進行預運算
顯然,Batch Layer執行的是批量處理,例如Hadoop或者Spark支持的Map-Reduce方式。 它的執行方式可以用一段偽代碼來表示:
function runBatchLayer(): while (true): recomputeBatchViews()
例如這樣一段代碼:
Api.execute(Api.hfsSeqfile("/tmp/pageview-counts"), new Subquery("?url", "?count") .predicate(Api.hfsSeqfile("/data/pageviews"), "?url", "?user", "?timestamp") .predicate(new Count(), "?count");
代碼并行地對hdfs文件夾下的page views進行統計(count),合并結果,并將最終結果保存在pageview-counts文件夾下。
利用Batch Layer進行預運算的作用實際上就是將大數據變小,從而有效地利用資源,改善實時查詢的性能。但這里有一個前提,就是我們需要預先知道查詢需要的數據,如此才能在Batch Layer中安排執行計劃,定期對數據進行批量處理。此外,還要求這些預運算的統計數據是支持合并(merge)的。
Serving Layer
Batch Layer通過對master dataset執行查詢獲得了batch view,而Serving Layer就要負責對batch view進行操作,從而為最終的實時查詢提供支撐。因此Serving Layer的職責包含:
對batch view的隨機訪問
更新batch view
Serving Layer應該是一個專用的分布式數據庫,例如Elephant DB,以支持對batch view的加載、隨機讀取以及更新。注意,它并不支持對batch view的隨機寫,因為隨機寫會為數據庫引來許多復雜性。簡單的特性才能使系統變得更健壯、可預測、易配置,也易于運維。
Speed Layer
只要batch layer完成對batch view的預計算,serving layer就會對其進行更新。這意味著在運行預計算時進入的數據不會馬上呈現到batch view中。這對于要求完全實時的數據系統而言是不能接受的。要解決這個問題,就要通過speed layer。從對數據的處理來看,speed layer與batch layer非常相似,它們之間最大的區別是前者只處理最近的數據,后者則要處理所有的數據。另一個區別是為了滿足最小的延遲,speed layer并不會在同一時間讀取所有的新數據,相反,它會在接收到新數據時,更新realtime view,而不會像batch layer那樣重新運算整個view。speed layer是一種增量的計算,而非重新運算(recomputation)。
因而,Speed Layer的作用包括:
對更新到serving layer帶來的高延遲的一種補充
快速、增量的算法
最終Batch Layer會覆蓋speed layer
Speed Layer的等式表達如下所示:
realtime view = function(realtime view, new data)
注意,realtime view是基于新數據和已有的realtime view。
總結下來,Lambda架構就是如下的三個等式:
batch view = function(all data)realtime view = function(realtime view, new data)query = function(batch view . realtime view)
整個Lambda架構如下圖所示:
基于Lambda架構,一旦數據通過batch layer進入到serving layer,在realtime view中的相應結果就不再需要了。