The Log(我所讀過的最好的一篇分佈式技術文章)

前言

這是一篇學習筆記。
學習的材料來自Jay Kreps的一篇講Log的博文。
原文很長,可是我堅持看完了,收穫頗多,也深深爲Jay哥的技術能力、架構能力和對於分佈式系統的理解之深入所折服。同時也由於某些理解和Jay哥觀點吻合而略沾沾自喜。java

Jay Kreps是前Linkedin的Principal Staff Engineer,現任Confluent公司的聯合創始人和CEO,Kafka和Samza的主要做者。python

所謂筆記,就是看了文章,提筆就記,由於Jay哥自己本章組織的太好,而其自己的科學素養及哲學素養也很高,因此私覺得出彩的東西就不省略了。程序員

1、資料來源

The Log: What every software engineer should know about real-time data's unifying abstraction算法

2、筆記

2.1 Log的價值

1) Log是以下系統的核心:數據庫

  • 分佈式圖數據庫
  • 分佈式搜索引擎
  • Hadoop
  • 第一代和第二代K-V數據庫

2) Log可能跟計算機的歷史同樣長,而且是分佈式數據系統和實時計算系統的核心。
3) Log的名字不少:編程

  • Commit log
  • Transaction log
  • Write-ahead log

4) 不理解Log,你就不可能充分理解緩存

  • 數據庫
  • NoSQL存儲
  • K-V存儲
  • 複製
  • Paxos算法
  • Hadoop
  • Version Control
  • 或者,任何軟件系統

2.2 什麼是Log?

2.2.1 概述

 

 

  • 記錄會附加到log的尾部。
  • 從左到右讀取記錄。
  • 每一個entry都有惟一且有序的log entry 序號。

記錄的順序定義了這樣的一個概念:時間。
由於越靠左的記錄越早。
Entry的序號能夠看成一種時間戳,將記錄的順序看成時間這一律念看起來很奇怪,可是很快你就會發現,這樣作:能夠方便地將「時間」與任一特定的物理時鐘解耦。
Log和常見的文件、表(table)沒有那麼大的差異。安全

  • 文件是一組字節
  • 表是一組記錄
  • Log能夠說是某種將記錄按時間排序的文件或者表

這樣說,可能你會以爲log如此簡單,還有討論的必要嗎?
其實,log的核心意義在於:服務器

Log記錄了什麼時候發生了什麼(they record what happened and when.)。

而這一條,一般是分佈式系統最最最核心的東西。
注意,這裏有必要澄清幾個概念:網絡

  • 本篇所討論的Log和程序員一般接觸的應用日誌(application logs)不一樣
  • 應用日誌一般是一種非結構化的,記錄錯誤信息、調試信息,用於追蹤應用的運行的,給人看的日誌,好比經過log4j或者 syslog來寫入本地文件的日誌。
  • 而本篇所討論的log是經過編程方式訪問的,不是給人看的,好比「journal」、「data logs」。
  • 應用日誌是本篇所討論的log的一種特化。

2.2.2 數據庫中的Logs

Log的起源不得而知,就像發明二分查找的人,難以意識到這種發明是一種發明。
Log的出現和IBM的System R 同樣早。
在數據庫中,須要在數據庫崩潰時,保持多種多樣的數據結構和索引保持同步。
爲保證原子性和持久性,數據庫須要在對數據結構和索引進行修改提交以前,記錄其要修改的內容。
因此log記錄了什麼時候發生了什麼,而每一張表和索引自己,都是這種歷史信息的映射。
由於log是當即持久化的,因此當crash發生時,其成爲恢復其它持久化結構的可靠來源。

Log從保證ACID特性的一種實現,發展成了一種數據庫之間數據複製的手段。

很顯然,數據庫中發生的一系列的數據變動,成爲數據庫之間 保持同步最須要的信息。
Oracle、MySQL、PostgreSQL,都包含了log傳輸協議,將log的一部分發送到用於保持複製的從數據庫(Slave)。
Oracle的XStreams和GoldenState,將log看成一種通用的數據訂閱機制,以提供給非Oracle的數據庫訂閱數據。
MySQL和PostgreSQL也提供了相似的組件,這些組件是數據系統架構的核心。
面向機器的Log,不只僅可被用在數據庫中,也能夠用在:

  • 消息系統
  • 數據流(data flow)
  • 實時計算

2.2.3 分佈式系統中的logs

Log解決了兩個很重要的分佈式數據系統中的問題:
1) 有序的數據變化
2) 數據分佈式化

所謂的狀態機複製原理(State Machine Replication Principle):

若是兩個肯定的處理過程,從相同的狀態開始,按照相同的順序,接收相同的輸入,那麼它們將會產生相同的輸出,並以 相同的狀態結束。

所謂肯定的(deterministic),是指處理過程是時間無關的,其處理結果亦不受額外輸入的影響。
能夠經過非肯定的例子來理解:

  • 多線程的執行順序不一樣致使不一樣的結果
  • 執行getTimeOfDay()方法
  • 其它的不能重複的處理過程

所謂狀態,能夠是機器上的任意數據,不管在處理結束後,是在機器的內存中仍是磁盤上。
相同的輸入按照相同的順序,產生相同的結果,這一點值得引發你的注意,這也是爲何log會如此重要,這是一個直覺性的概念:若是你將同一個log輸入兩個肯定性的程序,它們將產生相同的輸出。
在分佈式系統的構建中,意識到這一點,可使得:
讓全部的機器作一樣的事,規約爲:
構建分佈式的、知足一致性的log系統,覺得全部處理系統提供輸入。

Log系統的做用,就是將全部的輸入流之上的不肯定性驅散,確保全部的處理相同輸入的複製節點保持同步。

這種方法的最妙之處在於,你能夠將索引日誌的時間戳,做爲全部複製節點的時鐘來對待:

經過將複製節點所處理過的log中最大的時間戳,做爲複製節點的惟一ID,這樣,時間戳結合log,就能夠惟一地表達此節點的整個狀態。

應用這種方法的方式也不少:

  • 在log中記錄對一個服務的請求
  • 在回覆請求的先後,記錄服務狀態的變化
  • 或者,服務所執行的一系列轉換命令,等等。

理論上來說,咱們能夠記錄一系列的機器指令,或者所調用方法的名稱及參數,只要數據處理進程的行爲相同,這些進程就能夠保證跨節點的一致性。
常玩兒數據庫的人,會將邏輯日誌和物理日誌區分對待:

  • 物理日誌:記錄了全部的行內容的變化。
  • 邏輯日誌:不是記錄內容的變化,而是Insert , update , delete等致使行內容變化的SQL語句。

對分佈式系統,一般有兩種方式來處理複製和數據處理:
1) State machine model(active - active)
2) Primary-back model (active - passive)

以下圖所示:

 

爲了理解上述兩種方式的不一樣,來看個簡單的例子:
如今,集羣須要提供一個簡單的服務,來作加法、乘法等算術運算。初始,維護一個數字,好比0。

  • Active – active :在日誌記錄這樣的一些操做,如「+1」、「*2」等,這樣,每一個複製節點須要執行這些操做,以保證最後的數據狀態是一致的。
  • Active – passive:一個單獨的master節點,執行「+1」、「*2」等操做,而且在日誌中記錄操做的結果,如「1」、「3」、「6」等。

上面的例子也揭示了,爲何順序是複製節點之間保持一致性的關鍵因素,若是打亂了這些操做的順序,就會獲得不一樣的運算結果。
分佈式log,能夠當作某些一致性算法的數據結構:

  • Paxos
  • ZAB
  • RAFT
  • Viewstamped Replication

一條log,表徵了一系列的關於下一個值是什麼的決定。

2.2.4 Changelog

從數據庫的角度來看,一組記錄數據變化的changelog和表,是對偶和互通的。
1) 依據記錄了數據變化的log,能夠重構某一狀態的表(也能夠是非關係型存儲系統中有key的記錄)
2) 相反,表若是發生了變化,能夠將變化計入log。

這正是你想要的準實時複製的祕籍所在!

這一點和版本控制所作的事情極爲相似:管理分佈式的、併發的、對狀態進行的修改。

版本控制工具,維護了反映修改的補丁,這其實就是log,你和一個被簽出(checked out)的分支快照進行交互,這份快照就至關於數據庫中的表。你會發現,版本控制與分佈式系統中,複製都是基於log的:當你更新版本時,你只是拉取了反映了版本變化的補丁,並應用於當前的分支快照。

2.3 數據集成(Data integration)

2.3.1 數據集成的含義

所謂數據集成,就是將一個組織中的全部服務和系統的數據,變得可用。

實際上,對數據進行有效利用,很符合馬斯洛的層次需求理論。
金字塔的最底層,是收集數據,將其整合進應用系統中(不管是實時計算引擎,仍是文本文件,仍是python腳本)。
而這些數據,須要通過轉換,保持一個統1、規範、整潔的格式,以易於被讀取和處理。
當上面的要求被知足後,就能夠開始考慮多種多樣的數據處理方式,好比map – reduce 或者實時查詢系統。
很顯然,若是沒有一個可靠的、完備的數據流,Hadoop就僅僅是一個昂貴的、難以整合的加熱器(集羣很費電麼?)。
相反,若是能保證數據流可靠、可用且完備,就能夠考慮更高級的玩法、更好的數據模型和一致的、更易被理解的語義。
接着,注意力就能夠轉移到可視化、報表、算法和預測上來(挖啊機啊深度啊)。

2.3.2 數據集成的兩個複雜性

事件

事件數據,記錄了事件是怎麼發生的,而不只僅是發生了什麼,這一類log一般被當作應用日誌,由於通常是由應用系統寫入的。但這一點,其實混淆了log的功能。
Google的財富,其實,是由一個創建在(用戶)點擊流和好惡印象(體驗)之上的相關性pipeline產生的,而點擊流和印象,就是事件。

各類各樣的專業數據系統的爆發

這些系統存在的緣由:

  • 聯機分析(OLAP)
  • 搜索
  • 簡單的在線存儲
  • 批處理
  • 圖譜分析
  • 等等(如spark)

顯然,要將數據整合進這樣的系統中,對於數據集成來說,極爲困難。

2.3.3 基於日誌結構的數據流

每種邏輯意義上的數據源,均可以依據log進行建模。

數據源能夠是記錄了事件(點擊和PV)的應用程序,能夠是接受更改的數據庫表。

每一個訂閱者,都儘量快地從這些數據源產生的log中獲取新的記錄,應用於本地的存儲系統,而且提高其在log中的讀取偏移(offset)。訂閱者能夠是任何數據系統,好比緩存、Hadoop、另外一個站點的數據庫,或者搜索引擎。

Log,實際上提供了一種邏輯時鐘,針對數據變化,能夠測量不一樣的訂閱者所處的狀態,由於這些訂閱者在log中的讀取偏移不一樣且相互獨立,這種偏移就像一個時間意義上的「時刻」同樣。

 

 

考慮這樣一個例子,一個數據庫,和一些緩存服務器:
Log提供了這樣一種能力,可使得全部的緩存服務器獲得同步,並推出它們所處的「時刻」。

假設咱們寫入了一個編號爲X的log,要從某個緩存服務器讀取數據,爲了避免讀到老數據,只須要保證:在緩存服務器將數據(同步)複製到X這個位置前,咱們不從這個緩存中讀取任何東西便可。

此外,log還提供了做爲緩衝區的能力,以支持生產者和消費者的行爲以異步的方式進行。

最關鍵的一個支持異步的緣由,是訂閱系統可能會發生崩潰、因維護而下線,接着恢復上線,而在這種狀況下,每一個訂閱者都以本身的步調消費數據。

一個批處理系統,好比Hadoop,或者一個數據倉庫,是以小時或天爲單位消費數據,而一個實時系統,一般在秒級消費數據。
而數據源或者log,對消費數據的訂閱者一無所知,因此,須要在pipeline中作到無縫的添加訂閱者和移除訂閱者。

更重要的是,訂閱者,只須要知道log,而不須要對其所消費的數據的來源有任何瞭解,不管這個數據源是RDBMS、Hadoop,仍是一個最新流行的K-V數據庫,等等。

之因此討論log,而不是消息系統,是由於不一樣的消息系統所保證的特性不一樣,而且用消息系統這個詞,難以全面和精確表達某種語義,由於消息系統,更重要的在於重定向消息。

可是,能夠將log理解爲這樣一種消息系統,其提供了持久性保證及強有序的語義,在通信系統中,這稱做原子廣播。

2.4 在Linkedin

Linkedin目前的主要系統包括(注:2013年):

  • Search
  • Social Graph
  • Voldemort (K-V存儲)
  • Espresso (文檔存儲)
  • Recommendation engine
  • OLAP query engine
  • Hadoop
  • Terradata
  • Ingraphs (監控圖譜及metrics服務)

每一個系統,都在其專業的領域提供專門的高級功能。

(這一段太長太長了,Jay兄十分能侃啊,因此挑重點的來記吧!)

1) 之因此引入數據流這個概念,是由於要在oracle數據庫的表之上,創建一個抽象的緩存層,爲搜索引擎的索引構建和社交圖譜更新,提供拓展能力。

2) 爲了更好的處理linkedin的一些推薦算法,開始搭Hadoop集羣,但團隊在此塊的經驗尚淺,因此走了不少彎路。

3) 開始時,簡單粗暴地認爲只要將數據從oracle數據倉庫中拉出來,丟進hadoop就能夠了。結果發現:第一,將數據從oracle數據倉庫快速導出是個噩夢;第二,也是更糟糕的一點,數據倉庫中某些數據的處理不對,致使了hadoop的批處理任務不能按預期輸出結果,且經過hadoop批處理執行任務,一般不可逆,特別是在出了報表以後。

4) 最後,團隊拋棄了從數據倉庫中出數據的方式,直接以數據庫和logs爲數據源。接着,造出了一個輪子:K-V 存儲(Voldemort)。

5) 即便是數據拷貝這樣不高大上的活兒,也佔據了團隊大量的時間去處理,更糟的是,一旦數據處理的pipeline中有個點出錯,hadoop立馬變得廢柴,由於再牛逼的算法跑在錯誤的數據上,只有一個後果,就是產生更多的錯誤數據。

6) 即便團隊構建的東西抽象層次很高,針對每種數據源仍是須要特定的配置,而這也是不少錯誤和失敗的根源。

7) 一大批程序員想跟進,每一個程序員都有一大批的想法,集成這個系統,添加這個功能,整合這個特點,或者想要自定義的數據源。

8) Jay哥開始意識到:
第一, 雖然他們構建的pipelines還很糙,可是卻極其有價值。即便是解決了數據在新的系統(如hadoop)中可用的問題,也解鎖了一大批可能性。之前難作的計算開始變爲可能。新的產品和分析,僅須要解鎖其它系統中的數據,而且進行整合,就能夠容易地作出來。

第二, 很明顯,可靠地數據裝載須要更堅實的支撐,若是可以捕獲全部的結構,就可讓hadoop數據裝載徹底自動化,不須要加入新的數據源或人工修改數據的模式。數據會神奇地出如今HDFS中,而新的數據源加入後,Hive的表會用合適的列自動化地、自適應地生成。

第三,數據覆蓋度遠遠不足。由於要處理不少新的數據源,很難。

9) 爲了解決新數據源加入後的數據裝載問題,團隊開始了這樣的嘗試:

 

 

很快,他們發現這樣搞行不通,由於發佈和訂閱、生產和消費,數據流一般仍是雙向的,這成了一個O(n^2)的問題。
因此,他們須要的是這樣的模型:

 

 

須要將每一個消費者從數據源隔離,理想的狀況下,這些消費者只和一個data repository進行交互,而這個repository能夠提供它們訪問任意數據的能力。

10)消息系統 + log = Kafka,kafka橫空出世。

2.5 Log和ETL、數據倉庫的關係

2.5.1 數據倉庫

1) 一個裝有乾淨的、結構化的、集成的數據repository,用於分析。
2) 雖然想法很美好,可是獲取數據的方式有點過期了:週期性地從數據庫獲取數據,將其轉換爲某種可讀性更佳的格式。
3) 以前的數據倉庫問題在於:將乾淨的數據和數據倉庫高度耦合

數據倉庫,應該是一組查詢功能的集合,這些功能服務於報表、搜索、ad hot 分析,包含了計數(counting)、聚合(aggregation)、過濾(filtering)等操做,因此更應該是一個批處理系統。

可是將乾淨的數據和這樣的一種批處理系統高度耦合在一塊兒,意味着這些數據不能被實時系統消費,好比搜索引擎的索引構建、實時計算和實時監控系統,等等。

2.5.2 ETL

Jay哥認爲,ETL無非作兩件事:

1) 對數據進行抽取和清洗,將數據從特定的系統中解鎖
2) 重構數據,使其能經過數據倉庫進行查詢。好比將數據類型變爲適配某個關係型數據庫的類型,將模式轉換爲星型或者雪花模式,或者將其分解爲某種面向列的存儲格式。

可是,將這兩件事耦合在一塊兒,問題很大,由於集成後的、乾淨的數據,本應能被其它實時系統、索引構建系統、低延時的處理系統消費。

數據倉庫團隊,負責收集和清洗數據,可是,這些數據的生產者每每由於不明確數據倉庫團隊的數據處理需求,致使輸出很難被抽取和清洗的數據。
同時,由於核心業務團隊對和公司的其它團隊保持步調一致這件事兒不敏感,因此真正能處理的數據覆蓋度很低,數據流很脆弱,很難快速應對變化。

因此,更好的方式是:

 

 

若是想在一個乾淨的數據集上作點搜索、實時監控趨勢圖、實時報警的事兒,以原有的數據倉庫或者hadoop集羣來做爲基礎設施,都是不合適的。更糟的是,ETL所構建的針對數據倉庫的數據加載系統,對其它(實時)系統點兒用沒有。

最好的模型,就是在數據發佈者發佈數據以前,就已經完成了數據的清洗過程,由於只有發佈者最清楚它們的數據是什麼樣的。而全部在這個階段所作的操做,都應該知足無損和可逆

全部豐富語義、或添加值的實時轉換,都應在原始的log發佈後處理(post-processing),包括爲事件數據創建會話,或者添加某些感興趣的字段。原始的log依舊可被單獨使用,可是此類實時應用也派生了新的參數化的log。

最後,只有對應於具體的目標系統的數據聚合操做,應做爲數據裝載的一部分,好比轉換爲星型或雪花型模式,以在數據倉庫中進行分析和出報表。由於這個階段,就像傳統的ETL所作的那樣,由於有了很是乾淨和規範的數據流,(有了log後)如今變得很是簡單。

2.6 Log文件和事件

以log爲核心的架構,還有個額外的好處,就是易於實現無耦合的、事件驅動的系統。

傳統的 捕獲用戶活動和系統變化的方式,是將此類信息寫入文本日誌,而後抽取到數據倉庫或者hadoop集羣中進行聚合和處理,這個問題和前面所述的數據倉庫和ETL問題相似:數據與數據倉庫的高度耦合。

在Linkedin,其基於kafka構建了事件數據處理系統。爲各類各樣的action定義了成百上千種事件類型,從PV、用戶對於廣告的趕腳(ad impressions)、搜索,到服務的調用和應用的異常,等等。

爲了體會上述事件驅動系統的好處,看一個簡單的關於事件的例子:
在工做機會頁面上,提供一個機會。這個頁面應該只負責如何展現機會,而不該該過多地包含其它邏輯。可是,你會發現,在一個具備至關規模的網站中,作這件事,很容易就會讓愈來愈多的與展現機會無關的邏輯牽扯進來。

好比,咱們但願集成如下系統功能:
1) 咱們須要將數據發送到hadoop和數據倉庫作離線處理。
2) 咱們須要統計頁面瀏覽次數,以確保某些瀏覽不是爲了抓取網頁內容什麼的。
3) 咱們須要聚合對此頁面的瀏覽信息,在機會發布者的分析頁面上呈現。
4) 咱們須要記錄某用戶對此頁面的瀏覽記錄,以確保咱們對此用戶提供了有價值的、體驗良好的任何適宜此用戶的工做機會,而不是對此用戶一遍又一遍地重複展現某個機會(想一想老婆不在家才能玩的遊戲吧,那紅綠藍閃爍的特效,配合那勁爆的DJ風舞曲,或者那搖擺聚焦的事業峯和齊X小短裙的girls,而後點進去才發現是標題黨的ad吧!)。
5) 咱們的推薦系統須要記錄對此頁面的瀏覽記錄,以正確地追蹤此工做機會的流行度。

很快,僅僅展現機會的頁面邏輯,就會變得複雜。當咱們在移動端也增長了此機會的展現時,不得不把邏輯也遷移過去,這又加重了複雜程度。還沒完,糾結的東西是,負責處理此頁面的工程師,須要有其它系統的知識,以確保上述的那些功能能正確的集成在一塊兒。

這只是個極其簡單的例子,在實踐中,狀況只會更加複雜。
事件驅動可讓這件事變得簡單。

負責呈現機會的頁面,只須要呈現機會並記錄一些和呈現相關的因素,好比工做機會的相關屬性,誰瀏覽了這個頁面,以及其它的有用的與呈現相關的信息。頁面不須要保持對其它系統的知識和了解,好比推薦系統、安全系統、機會發布者的分析系統,還有數據倉庫,全部的這些系統只須要做爲訂閱者,訂閱這個事件,而後獨立地進行它們各自的處理便可,而呈現機會的頁面不須要由於新的訂閱者或消費者的加入而作出修改。

2.7 構建可擴展的log

分離發佈者和訂閱者不新鮮,可是要保證多個訂閱者可以實時處理消息,而且同時保證擴展能力,對於log系統來講,是一件比較困難的事。

若是log的構建不具有快速、低開銷和可擴展能力,那麼創建在此log系統之上的一切美好都免談。

不少人可能認爲log系統在分佈式系統中是個很慢、重型開銷的活兒,而且僅用來處理一些相似於ZooKeeper更適合處理的元數據等信息。

可是Linkedin如今(注:2013年),在kafka中天天處理600億條不一樣的消息寫入(若是算數據中心的鏡像的話,那就是幾千億條寫入)。

Jay哥他們怎麼作到的呢?

1) 對log進行分割(partitioning the log)
2) 經過批量讀寫優化吞吐量
3) 避免沒必要要的數據拷貝

經過將log切爲多個partition來提供擴展能力:

 

 

1) 每一個partition都是有序的log,可是partitions之間沒有全局的順序。

2) 將消息寫入哪一個partition徹底由寫入者控制,經過依照某種類型的key(如user_id)進行分割。

3) 分割使得log的附加操做,能夠不用在分片(sharding)之間進行協調就進行,同時,保證系統的吞吐量和kafka集羣的規模呈線性關係。

4) 雖然沒有提供全局順序(實際上消費者或者訂閱者成千上萬,討論它們的全局順序通常沒有啥價值),可是kafka提供了這樣一種保證:發送者按照什麼順序將消息發給某個partition,從這個partition遞交出去的消息就是什麼順序(什麼順序進,什麼順序出)。

5) 每一個partition都按照配置好的數目進行復制,若是一個leader節點掛了,其它的節點會成爲新的leader。

6) 一條log,同文件系統同樣,線性的讀寫模式可被優化,將小的讀寫log能夠組成更大的、高吞吐量的操做。Kafka在這件事上作的很猛。批處理用在了各類場景之下,好比客戶端將數據發送到服務端、將數據寫入磁盤、服務器之間的數據複製、將數據傳送給消費者,以及確認提交數據等場景。

7) 最後,kafka在內存log、磁盤log、網絡中發送的log上,採用了很簡單的二進制格式,以利於利用各類優化技術,好比零拷貝數據傳輸技術(zero-copy data transfer)。

諸多的優化技術,匯聚起來,可讓你即便在內存爆滿的情形下,也能按照磁盤或網絡能提供的最大能力進行數據讀寫。

2.8 Logs和實時處理

你覺得Jay哥提供了這麼個美麗的方法把數據複製來複制去就完了?
你!錯!了!

Log是流的另外一種說法,logs是流處理的核心。

2.8.1 什麼是流處理

Jay哥認爲:
1)流處理是連續數據處理的基礎設施。
2)流處理的計算模型,就如同MapReduce或其它分佈式處理框架同樣,只是須要保證低延遲。
3)批處理式的收集數據模式,致使了批處理式的數據處理模式。
4)連續的收集數據模式,致使了連續的數據處理模式。
5)Jay哥講了個美國人口普查的方式來解釋批處理。

在linkedin,不管是活動數據仍是數據庫的變化,都是連續的。
批處理按天處理數據,和連續計算將窗口設爲一天雷同。

因此,流處理是這樣一種過程:
6)在處理數據時,帶了一個時間的概念,不須要對數據保持一個靜態的快照,因此能夠在用戶自定義的頻率之下,輸出結果,而沒必要等數據集到達某種「結束」的狀態。
7)從這個意義上講,流處理是批處理的一種泛化,而且考慮到實時數據的流行程度,這是一種極其重要的泛化。
8)許多商業公司沒法創建流處理引擎,每每由於沒法創建流數據收集引擎。
9)流處理跨越了實時響應式服務和離線批處理的基礎設施之間的鴻溝。
10)Log系統,解決了不少流處理模式中的關鍵問題,其中最大的一個問題就是如何在實時的多個訂閱者模式下,提供可用數據的問題(流數據收集)。

2.9 數據流圖譜

流處理中最有趣的地方在於,其拓展了什麼是數據源(feeds)這一律念。
不管是原始數據的logs、feeds,仍是事件、一行一行的數據記錄,都來自應用程序的活動。
可是,流處理還可讓咱們處理來自其它feeds的數據,這些數據和原始數據,在消費者看來,並沒有二致,而這些派生的feeds能夠包含任意程度的複雜性。

 

 

一個流處理任務,應該是這樣的:從logs讀取數據,將輸出寫入logs或者其它系統。

做爲輸入和輸出的logs,連通這些處理自己,和其它的處理過程,構成了一個圖。

事實上,以log爲核心的系統,容許你將公司或機構中的數據捕獲、轉換以及數據流,看做是一系列的logs及在其上進行寫入的處理過程的結合。

一個流處理程序,其實沒必要很高大上:能夠是一個處理過程或者一組處理過程,可是,爲了便於管理處理所用的代碼,能夠提供一些額外的基礎設施和支持。

引入logs有兩個目的:

1) 保證了數據集能夠支持多個訂閱者模式,及有序。
2) 能夠做爲應用的緩衝區。這點很重要,在非同步的數據處理進程中,若是上游的生產者出數據的速度更快,消費者的速度跟不上,這種狀況下,要麼使處理進程阻塞,要麼引入緩衝區,要麼丟棄數據。
丟棄數據彷佛不是個好的選擇,而阻塞處理進程,會使得全部的數據流的處理圖譜中的處理進程卡住。而log,是一種很大,特大,很是大的緩衝區,它容許處理進程的重啓,使得某個進程失敗後,不影響流處理圖譜中的其它進程。這對於一個龐大的機構去擴展數據流是很是關鍵的,由於不一樣的團隊有不一樣的處理任務,顯然不能由於某個任務發生錯誤,整個流處理進程都被卡住。

Storm和Samza就是這樣的流處理引擎,而且都能用kafka或其它相似的系統做爲它們的log系統。

(注:Jay哥至關猛,前有kafka,後有samza。)

2.10 有狀態的實時處理

不少流處理引擎是無狀態的、一次一記錄的形式,但不少用例都須要在流處理的某個大小的時間窗口內進行復雜的counts , aggregations和joins操做。
好比,點擊流中,join用戶信息。

那麼,這種用例,就須要狀態的支持。在處理數據的地方,須要維護某個數據的狀態。

問題在於,如何在處理者可能掛掉的狀況下保持正確的狀態?

將狀態維護在內存中多是最簡單的,但抵不住crash。

若是僅在某個時間窗口內維護狀態,當掛掉或者失敗發生,那麼處理能夠直接回退到窗口的起點來重放,可是,若是這個窗口有1小時那麼長,這可能行不通。

還有個簡單的辦法,就是把狀態存在某個遠程的存儲系統或數據庫中,可是這會損失數據的局部性併產生不少的網絡間數據往返(network round-trip)。

回憶下,上文中曾提到的數據庫中的表和log的對偶性
一個流處理組件,可使用本地的存儲或索引來維護狀態:

  • Bdb
  • Leveldb
  • Lucene
  • Fastbit

經過記錄關於本地索引的changelog,用於在crash後恢復狀態。這種機制,其實也揭示了一種通常化的,能夠存儲爲任意索引類型的,與輸入流同時被分割(co-partitioned)的狀態。

當處理進程崩潰,其能夠從changelog中恢復索引,log充當了將本地狀態轉化爲某種基於時間備份的增量記錄的角色。

這種機制還提供了一種很優雅的能力:處理過程自己的狀態也能夠做爲log被記錄下來,顯然,其它的處理過程能夠訂閱這個狀態。

結合數據庫中的log技術,針對數據集成這一場景,每每能夠作出很強大的事:

將log從數據庫中抽取出來,並在各類各樣的流處理系統中進行索引,那麼,與不一樣的事件流進行join就成爲可能。

2.11 Log 合併

顯然,用log記錄全時全量的狀態變動信息,不太可能。

Kafka使用了log合併或者log垃圾回收技術:

1) 對於事件數據,kafka只保留一個時間窗口(可在時間上配置爲幾天,或者按空間來配置)
2) 對於keyed update,kafka採用壓縮技術。此類log,能夠用來在另外的系統中經過重放技術來重建源系統的狀態。

若是保持全時全量的logs,隨着時間增加,數據將會變得愈來愈大,重放的過程也會愈來愈長。
Kafka不是簡單地丟棄老的日誌信息,而是採用合併的方式,丟棄廢棄的記錄,好比,某個消息的主鍵最近被更新了。

 

 

2.12 系統構建

2.12.1 分佈式系統

Log,在分佈式數據庫的數據流系統和數據集成中所扮演的角色是一致的:

  • 抽象數據流
  • 保持數據一致性
  • 提供數據恢復能力

你能夠將整個機構中的應用系統和數據流,看做是一個單獨的分佈式數據庫。
將面向查詢的獨立系統,好比Redis , SOLR , Hive tables 等等,看做是一種特別的、數據之上的索引。
將Storm、Samza等流處理系統,看作一種精心設計過的觸發器或者物化視圖機制。

各式各樣的數據系統,爆發性的出現,其實,這種複雜性早已存在。
在關係型數據庫的輝煌時期(heyday),某個公司或者機構光關係型數據庫就有不少種。

顯然,不可能將全部的東西都丟進一個Hadoop集羣中,指望其解決全部的問題。因此,如何構建一個好的系統,可能會像下面這樣:

構建一個分佈式系統,每一個組件都是一些很小的集羣,每一個集羣不必定能完整提供安全性、性能隔離、或者良好的擴展性,可是,每一個問題都能獲得(專業地)解決。

Jay哥以爲,之因此各式各樣的系統爆發性地出現,就是由於要構建一個強大的分佈式系統十分困難。而若是將用例限制到一些簡單的,好比查詢這樣的場景下,每一個系統都有足夠的能力去解決問題,可是要把這些系統整合起來,很難。

Jay哥以爲在將來構建系統這事兒有三種可能:

1) 保持現狀。這種狀況下,數據集成依然是最頭大的問題,因此一個外部的log系統就很重要(kafka!)
2) 出現一個強大的(如同輝煌時期的關係型數據庫)能解決全部問題的系統,這彷佛有點不可能發生。
3) 新生代的系統大部分都開源,這揭示了第三種可能:數據基礎設施可被離散爲一組服務、以及面向應用的系統API,各種服務各司其事,每一個都不完整,卻能專業滴解決專門的問題,其實經過現存的java技術棧就能看出端倪:

  • ZooKeeper:解決分佈式系統的同步、協做問題(也可能受益於更高抽象層次的組件如helix、curator).
  • Mesos、YARN:解決虛擬化和資源管理問題。
  • 嵌入式的組件Lucene、LevelDB:解決索引問題。
  • Netty、Jetty及更高抽象層次的Finagle、http://rest.li解決遠程通信問題。
  • Avro、Protocol Buffers、Thrift及umpteen zlin:解決序列化問題。
  • Kafka、bookeeper:提供backing log能力。

從某種角度來看,構建這樣的分佈式系統,就像某個版本的樂高積木同樣。這顯然跟更關心API的終端用戶沒有太大關係,可是這揭示了構建一個強大系統並保持簡單性的一條道路:
顯然,若是構建一個分佈式系統的時間從幾年降到幾周,那麼構建一個獨立的龐大系統的複雜性就會消失,而這種狀況的出現,必定是由於出現了更可靠、更靈活的「積木」。

2.12.2 Log在系統構建中的地位

若是一個系統,有了外部log系統的支持,那麼每一個獨立的系統就能夠經過共享log來下降其自身的複雜性,Jay哥認爲log的做用是:

1) 處理數據一致性問題。不管是當即一致性仍是最終一致性,均可以經過序列化對於節點的併發操做來達到。

2) 在節點間提供數據複製。

3) 提供「提交」的語義。好比,在你認爲你的寫操做不會丟失的狀況下進行操做確認。

4) 提供外部系統可訂閱的數據源(feeds)。

5) 當節點因失敗而丟失數據時,提供恢復的能力,或者從新構建新的複製節點。

6) 處理節點間的負載均衡。

以上,大概是一個完整的分佈式系統中應提供的大部分功能了(Jay哥確實愛Log!),剩下的就是客戶端的API和諸如一些構建索引的事了,好比全文索引須要獲取全部的partitions,而針對主鍵的查詢,只須要在某個partition中獲取數據。

(那把剩下的事情也交代下吧,Jay哥威武!)

系統可被分爲兩個邏輯組件(這強大的理解和功力):

1) Log層
2) 服務層

Log層,以序列化的、有序的方式捕獲狀態的變化,而服務層,則存儲外部查詢須要的索引,好比一個K-V存儲可能須要B-tree、sstable索引,而一個搜索服務須要倒排索引。

寫操做既能夠直接入log層,也能夠經過服務層作代理。寫入log會產生一個邏輯上的時間戳(log的索引),好比一個數字ID,若是系統partition化了,那麼,服務層和log層會擁有相同的partitions(但其各自的機器數可能不一樣)。

 

 

服務層訂閱到log層,而且以最快的速度、按log存儲的順序追log,將數據和狀態變化同步進本身的本地索引中。

客戶端將會獲得read-your-write的語義:

經過對任一一個節點,在查詢時攜帶其寫入時的時間戳,服務層的節點收到此查詢,經過和其本地索引比較時間戳,若是必要,爲了防止返回過時的老數據,推遲請求的執行,直到此服務節點的索引同步跟上了時間戳。

服務層的節點,也許須要、也許不須要知道leader的概念。在不少簡單的用例中,服務層可不構建leader節點,由於log就是事實的來源。

還有一個問題,如何處理節點失敗後的恢復問題。能夠這樣作,在log中保留一個固定大小的時間窗口,同時對數據維護快照。也可讓log保留數據的全量備份並使用log合併技術完成log自身的垃圾回收。這種方法,將服務層的衆多複雜性移至log層,由於服務層是系統相關(system-specific)的,而log層確能夠通用。

基於log系統,能夠提供一組完備的、供開發使用的、可做爲其它系統的ETL數據源、並供其它系統訂閱的API。

Full Stack !:

 

 

顯然,一個以log爲核心的分佈式系統,其自己當即成爲了可對其它系統提供數據裝載支持及數據流處理的角色。一樣的,一個流處理系統,也能夠同時消費多個數據流,並經過對這些數據流進行索引而後輸出的另外一個系統,來對外提供服務。

基於log層和服務層來構建系統,使得查詢相關的因素與系統的可用性、一致性等因素解耦。

也許不少人認爲在log中維護數據的單獨備份,特別是作全量數據拷貝太浪費、太奢侈,但事實並不是如此:

1) linkedin(注:2013年)的kafka生產集羣維護了每數據中心75TB的數據,而應用集羣須要的存儲空間和存儲條件(SSD+更多的內存)比kafka集羣要高。
2) 全文搜索的索引,最好所有裝入內存,而logs由於都是線性讀寫,因此能夠利用廉價的大容量磁盤。
3) 由於kafka集羣實際運做在多個訂閱者的模式之下,多個系統消費數據,因此log集羣的開銷被攤還了。
4) 全部以上緣由,致使基於外部log系統(kafka或者相似系統)的開銷變得很是小。

2.13 結語

Jay哥在最後,不只厚道地留下了不少學術、工程上的有價值的論文和參考連接,還很謙遜地留下了這句話:

If you made it this far you know most of what I know about logs.

終。

相關文章
相關標籤/搜索