【編者按】日志幾乎與計算機同時產生的,它是許多分布式數據系統和實時應用結構的核心。但是大多數軟件工程師對他們不是很熟悉。在這里,我們將聚焦LinkedIn大數據平臺的深度解析,探討日志是什么,如何在數據集成、實時處理和系統構建中使用日志等內容。
免費訂閱“CSDN大數據”微信公眾號,實時了解最新的大數據進展!
CSDN大數據,專注大數據資訊、技術和經驗的分享和討論,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、機器學習、智能算法等相關大數據觀點,大數據技術,大數據平臺,大數據實踐,大數據產業資訊等服務。
以下為原文:
我在六年前的一個令人興奮的時刻加入到LinkedIn公司。從那個時候開始我們就破解單一的、集中式數據庫的限制,并且啟動到特殊的分布式系統套件的轉換。這是一件令人興奮的事情:我們構建、部署,而且直到今天仍然在運行的分布式圖形數據庫、分布式搜索后端、Hadoop安裝以及第一代和第二代鍵值數據存儲。
從這一切里我們體會到的最有益的事情是我們構建的許多東西的核心里都包含一個簡單的理念:日志。有時候也稱作預先寫入日志或者提交日志或者事務日志,日志幾乎在計算機產生的時候就存在,同時它還是許多分布式數據系統和實時應用結構的核心。
不懂得日志,你就不可能完全懂得數據庫,NoSQL存儲,鍵值存儲,復制,paxos,Hadoop,版本控制以及幾乎所有的軟件系統;然而大多數軟件工程師對它們不是很熟悉。我愿意改變這種現狀。在這篇博客文章里,我將帶你瀏覽你必須了解的有關日志的所有的東西,包括日志是什么,如何在數據集成、實時處理和系統構建中使用日志等。
第一部分:日志是什么?
日志是一種簡單的不能再簡單的存儲抽象。它是一個只能增加的,完全按照時間排序的一系列記錄。日志看起來如下:
我們可以給日志的末尾添加記錄,并且可以從左到右讀取日志記錄。每一條記錄都指定了一個唯一的有一定順序的日志記錄編號。
日志記錄的排序是由“時間”來確定的,這是因為位于左邊的日志記錄比位于右邊的要早些。日志記錄編號可以看作是這條日志記錄的“時間戳”。在一開始就把這種排序說成是按時間排序顯得有點多余 ,不過 ,與任何一個具體的物理時鐘相比,時間屬性是非常便于使用的屬性。在我們運行多個分布式系統的時候,這個屬性就顯得非常重要。
對于這篇討論的目標而言,日志記錄的內容和格式不怎么重要。另外提醒一下,在完全耗盡存儲空間的情況下,我們不可能再給日志添加記錄。稍后我們將會提到這個問題。
日志并不是完全不同于文件或者數據表的。文件是由一系列字節組成,表是由一系列記錄組成,而日志實際上只是按照時間順序存儲記錄的 一種數據表或者文件。
此時,你可能奇怪為什么要討論這么簡單的事情呢?不同環境下的一個只可增加的有一定順序的日志記錄是怎樣與數據系統關聯起來的呢?答案是日志有其特定的應用目標:它記錄了什么時間發生了什么事情。 而對分布式數據系統許多方面而言, 這才是問題的真正核心。
不過,在我們進行更加深入的討論之前,讓我先澄清有些讓人混淆的概念。每個編程人員都熟悉另一種日志記錄――應用使用syslog或者log4j可能寫入到本地文件里的沒有結構的錯誤信息或者追蹤信息。為了區分開來,我們把這種情形的日志記錄稱為“應用日志記錄”。應用日志記錄是我在這兒所說的日志的一種低級的變種。最大的區別是:文本日志意味著主要用來方便人們閱讀,而我所說明的“日志”或者“數據日志”的建立是方便程序訪問。
(實際上,如果你對它進行深入的思考,那么人們讀取某個機器上的日志這種理念有些不順應時代潮流。當涉及到許多服務和服務器的時候,這種方法很快就變成一個難于管理的方式,而且為了認識多個機器的行為,日志的目標很快就變成查詢和圖形化這些行為的輸入了――對多個機器的某些行為而言,文件里的英文形式的文本同這兒所描述的這種結構化的日志相比幾乎就不適合了。)
數據庫日志
我不知道日志概念起源于何處-可能它就像二進制搜索一樣:發明者認為它太簡單而不能當作一項發明。它早在IBM的系統R出現時候就出現了。數據庫里的用法是在崩潰的時候用它來同步各種數據結構和索引。為了保證操作的原子性和持久性,在對數據庫維護的所有各種數據結構做更改之前,數據庫把即將修改的信息謄寫到日志里。日志記錄了發生了什么,而且其中的每個表或者索引都是一些數據結構或者索引的歷史映射。由于日志是即刻永久化的,可以把它當作崩潰發生時用來恢復其他所有永久性結構的可信賴數據源。
隨著時間的推移,日志的用途從實現ACID細節成長為數據庫間復制數據的一種方法。利用日志的結果就是發生在數據庫上的更改順序與遠端復制數據庫上的更改順序需要保持完全同步。
Oracle,MySQL 和PostgreSQL都包括用于給備用的復制數據庫傳輸日志的日志傳輸協議。Oracle還把日志產品化為一個通用的數據訂閱機制,這樣非Oracle數據訂閱用戶就可以使用XStreams和GoldenGate訂閱數據了,MySQL和PostgreSQL上的類似的實現則成為許多數據結構的關鍵組件。
正是由于這樣的起源,機器可識別的日志的概念大部分都被局限在數據庫內部。日志用做數據訂閱的機制似乎是偶然出現的,不過要把這種 抽象用于支持所有類型的消息傳輸、數據流和實時數據處理是不切實際的。
分布式系統日志
日志解決了兩個問題:更改動作的排序和數據的分發,這兩個問題在分布式數據系統里顯得尤為重要。協商出一致的更改動作的順序(或者說保持各個子系統本身的做法,但可以進行存在副作用的數據拷貝)是分布式系統設計的核心問題之一。
以日志為中心實現分布式系統是受到了一個簡單的經驗常識的啟發,我把這個經驗常識稱為狀態機復制原理:如果兩個相同的、確定性的進程從同一狀態開始,并且以相同的順序獲得相同的輸入,那么這兩個進程將會生成相同的輸出,并且結束在相同的狀態。
這也許有點難以理解,讓我們更加深入的探討,弄懂它的真正含義。
確定性意味著處理過程是與時間無關的,而且任何其他“外部的“輸入不會影響到處理結果。例如,如果一個程序的輸出會受到線程執行的具體順序影響,或者受到gettimeofday調用、或者其他一些非重復性事件的影響,那么這樣的程序一般最有可能被認為是非確定性的。
進程狀態是進程保存在機器上的任何數據,在進程處理結束的時候,這些數據要么保存在內存里,要么保存在磁盤上。
以相同的順序獲得相同輸入的地方應當引起注意-這就是引入日志的地方。這兒有一個重要的常識:如果給兩段確定性代碼相同的日志輸入,那么它們就會生成相同的輸出。
分布式計算這方面的應用就格外明顯。你可以把用多臺機器一起執行同一件事情的問題縮減為實現分布式一致性日志為這些進程輸入的問題。這兒日志的目的是把所有非確定性的東西排除在輸入流之外,來確保每個復制進程能夠同步地處理輸入。
當你理解了這個以后,狀態機復制原理就不再復雜或者說不再深奧了:這或多或少的意味著“確定性的處理過程就是確定性的”。不管怎樣,我都認為它是分布式系統設計里較常用的工具之一。
這種方式的一個美妙之處就在于索引日志的時間戳就像時鐘狀態的一個副本――你可以用一個單獨的數字描述每一個副本,這就是經過處理的日志的時間戳。時間戳與日志一一對應著整個副本的狀態。
由于寫進日志的內容的不同,也就有許多在系統中應用這個原則的不同方式。舉個例子,我們記錄一個服務的請求,或者服務從請求到響應的狀態變化,或者它執行命令的轉換。理論上來說,我們甚至可以為每一個副本記錄一系列要執行的機器指令或者調用的方法名和參數。只要兩個進程用相同的方式處理這些輸入,這些進程就會保持副本的一致性。
一千個人眼中有一千種日志的用法。數據庫工作者通常區分物理日志和邏輯日志。物理日志就是記錄每一行被改變的內容。邏輯日志記錄的不是改變的行而是那些引起行的內容被改變的SQL語句(insert,update和delete語句)。
分布式系統通常可以寬泛分為兩種方法來處理數據和完成響應“狀態機器模型”通常引用一個主動―主動的模型――也就是我們為之記錄請求和響應的對象。對此進行一個細微的更改,稱之為“預備份模型”,就是選出一個副本做為leader,并允許它按照請求到達的時間來進行處理并從處理過程中輸出記錄其狀態改變的日志。其他的副本按照leader狀態改變的順序而應用那些改變,這樣他們之間達到同步,并能夠在leader失敗的時候接替leader的工作。
primary-Backup State Machine Replication
為了理解兩種方式的不同,我們來看一個不太嚴謹的例子。假定有一個算法服務的副本,保持一個獨立的數字作為它的狀態(初始值為0),并對這個值進行加法和乘法運算。主動―主動方式應該會輸出所進行的變換,比如“+1”,“*2”等。每一個副本都會應用這些變換,從而得到同樣的解集。主動―被動方式將會有一個獨立的主體執行這些變換并輸出結果日志,比如“1”,“3”,“6”等。這個例子也清楚的展示了為什么說順序是保證各副本間一致性的關鍵:一次加法和乘法的順序的改變將會導致不同的結果。
分布式日志可以理解為一致性問題模型的數據結構。因為日志代表了后續追加值的一系列決策。你需要重新審視Paxos算法簇,盡管日志模塊是他們最常見的應用。 在Paxos算法中,它通常通過使用稱之為多paxos的協議,這種協議將日志建模為一系列的問題,在日志中每個問題都有對應的部分。在ZAB, RAFT等其它的協議中,日志的作用尤為突出,它直接對維護分布式的、一致性的日志的問題建模。
我懷疑的是,我們就歷史發展的觀點是有偏差的,可能是由于過去的幾十年中,分布式計算的理論遠超過了其實際應用。在現實中,共識的問題是有點太簡單了。計算機系統很少需要決定單個值,他們幾乎總是處理成序列的請求。這樣的記錄,而不是一個簡單的單值寄存器,自然是更加抽象。
此外,專注于算法掩蓋了 抽象系統需要的底層的日志。我懷疑,我們最終會把日志中更注重作為一個商品化的基石,不論其是否以同樣的方式 實施的,我們經常談論一個哈希表而不是糾結我們得到是不是具體某個細節的哈希表,例如線性或者帶有什么什么其它變體哈希表。日志將成為一種大眾化的接口,為大多數算法和其實現提升提供最好的保證和最佳的性能。
變更日志101:表與事件的二相性
讓我們繼續聊數據庫。數據庫中存在著大量變更日志和表之間的二相性。這些日志有點類似借貸清單和銀行的流程,數據庫表就是當前的盈余表。如果你有大量的變更日志,你就可以使用這些變更用以創建捕獲當前狀態的表。這張表將記錄每個關鍵點(日志中一個特別的時間點)的狀態信息。這就是為什么日志是非常基本的數據結構的意義所在:日志可用來創建基本表,也可以用來創建各類衍生表。同時意味著可以存儲非關系型的對象。
這個流程也是可逆的:如果你正在對一張表進行更新,你可以記錄這些變更,并把所有更新的日志發布到表的狀態信息中。這些變更日志就是你所需要的支持準實時的克隆。基于此,你就可以清楚的理解表與事件的二相性: 表支持了靜態數據而日志捕獲變更。日志的魅力就在于它是變更的完整記錄,它不僅僅捕獲了表的最終版本的內容,它還記錄了曾經存在過的其它版本的信息。日志實質上是表歷史狀態的一系列備份。
這可能會引起你對源代碼的版本管理。源代碼管理和數據庫之間有密切關系。版本管理解決了一個大家非常熟悉的問題,那就是什么是分布式數據系統需要解決的――時時刻刻在變化著的分布式管理。版本管理系統通常以補丁的發布為基礎,這實際上可能是一個日志。您可以直接對當前類似于表中的代碼做出“快照”互動。你會注意到,與其他分布式狀態化系統類似,版本控制系統 當你更新時會復制日志,你希望的只是更新補丁并將它們應用到你的當前快照中。
最近,有些人從Datomic――一家銷售日志數據庫的公司得到了一些想法。這些想法使他們對如何在他們的系統應用這些想法有了開闊的認識。 當然這些想法不是只針對這個系統,他們會成為十多年分布式系統和數據庫文獻的一部分。
這可能似乎有點過于理想化。但是不要悲觀!我們會很快把它實現。
在這篇文章的其余部分,我將試圖說明日志除了可用在分布式計算或者抽象分布式計算模型內部之外,還可用在哪些方面。其中包括:
所有這些用法都是通過把日志用做單獨服務來實現的。
在上面任何一種用法里,日志的用途開始都是使用了日志所能提供的某個簡單功能:生成永久的、可重現的歷史記錄。令人意外的是,問題的核心是可以讓多少臺機器以特定的方式,按照自身的速度重現歷史記錄的能力。
第二部分:數據集成
請讓我首先解釋 一下“數據集成”是什么意思,還有為什么我覺得它很重要,之后我們再來看看它和日志有什么關系。
數據集成就是將數據組織起來,使得在與其有關的服務和系統中可以訪問它們。“數據集成”(data integration)這個短語應該不止這么簡單,但是我找不到一個更好的解釋。而更常見的術語 ETL 通常只是覆蓋了數據集成的一個有限子集(譯注:ETL,Extraction-Transformation-Loading的縮寫,即數據提取、轉換和加載)――相對于關系型數據倉庫。但我描述的東西很大程度上可以理解為,將ETL推廣至實時系統和處理流程。
你一定不會聽到數據集成就興趣盎然屏住呼吸,并且天花亂墜的想到關于大數據的概念,不過,我相信世俗的問題“讓數據可被訪問” 是一個組織應該關注的有價值的事情。
對數據的高效使用遵循一種馬斯洛的需要層次理論 。金字塔的基礎部分包括捕獲所有相關數據,能夠將它們全部放到適當的處理環境(那個環境應該是一個奇妙的實時查詢系統,或者僅僅是文本文件和python腳本)。這些數據需要以統一的方式建模,這樣就可以方便讀取和數據處理。如果這種以統一的方式捕獲數據的基本需求得到滿足,那么就可以在基礎設施上以若干種方法處理這些數據――映射化簡(MapReduce),實時查詢系統,等等。
很明顯,有一點值得注意:如果沒有可靠的、完整的數據流,Hadoop集群除了象昂貴的且難于安裝的空間取暖器哪樣外不會做更多事情了。一旦數據和處理可用,人們就會關心良好數據模型和一致地易于理解的語法哪些更細致的問題。最后,人們才會關注更加高級的處理――更好的可視化、報表以及處理和預測算法。
以我的經驗,大多數機構在數據金字塔的底部存在巨大的漏洞――它們缺乏可靠的、完整的數據流――而是打算直接跳到高級數據模型技術上。這樣做完全是反著來做的。
因此,問題是我們如何構建通過機構內所有數據系統的可靠的數據流。
數據集成:兩個并發癥
兩種趨勢使數據集成變得更困難。
事件數據管道
第一個趨勢是增長的事件數據(event data)。事件數據記錄的是發生的事情,而不是存在的東西。在web系統中,這就意味著用戶活動日志,還有為了可靠的操作以及監控數據中心的機器的目的,所需要記錄的機器級別的事件和統計數字。人們傾向稱它們為“日志數據”,因為它們經常被寫到應用的日志中,但是這混淆了形式與功能。這種數據位于現代web的中心:歸根結底,Google的資產是由這樣一些建立在點擊和映像基礎之上的相關管道所生成的――那也就是事件。
這些東西并不是僅限于網絡公司,只是網絡公司已經完全數字化,所以它們更容易用設備記錄。財務數據一直是面向事件的。RFID(無線射頻識別)將這種跟蹤能力賦予物理對象。我認為這種趨勢仍將繼續,伴隨著這個過程的是傳統商務活動的數字化。
這種類型的事件數據記錄下發生的事情,而且往往比傳統數據庫應用要大好幾個數量級。這對于處理提出了重大挑戰。
專門的數據系統的爆發
第二個趨勢來自于專門的數據系統的爆發,通常這些數據系統在最近的五年中開始變得流行,并且可以免費獲得。專門的數據系統是為OLAP,搜索, 簡單在線存儲, 批處理, 圖像分析,等等而存在的。
更多的不同類型數據的組合,以及將這些數據存放到更多的系統中的愿望,導致了一個巨大的數據集成問題。
日志結構數據流
為了處理系統之間的數據流,日志是最自然的數據結構。其中的秘訣很簡單:
將所有組織的數據提取出來,并將它們放到一個中心日志,以便實時查閱。
每個邏輯數據源都可以建模為它自己的日志。一個數據源可以是一個應用程序的事件日志(如點擊量或者頁面瀏覽量),或者是一個接受修改的數據庫表。每個訂閱消息的系統都盡可能快的從日志讀取信息,將每條新的記錄保存到自己的存儲,并且提升其在日志中的地位。訂閱方可以是任意一種數據系統 ―― 一個緩存,Hadoop,另一個網站中的另一個數據庫,一個搜索系統,等等。
例如,日志針對每個更改給出了邏輯時鐘的概念,這樣所有的訂閱方都可以被測量。推導不同的訂閱系統的狀態也因此變得相對簡單的多,因為每個系統都有一個讀取動作的“時間點”。
為了讓這個顯得更具體,我們考慮一個簡單的案例,有一個數據庫和一組緩存服務器集群。日志提供了一種同步更新所有這些系統,并推導出每一個系統的接觸時間點的方法。我們假設寫了一條日志X,然后需要從緩存做一次讀取。如果我們想保證看到的不是陳舊的數據,我們只需保證沒有從任何尚未復制X的緩存中讀取即可。
日志也起到緩存的作用,使數據生產與數據消費相同步。由于許多原因這個功能很重要,特別是在多個訂閱方消費數據的速度各不相同的時候。這意味著一個訂閱數據系統可以宕機,或者下線維護,之后重新上線以后再趕上來:訂閱方按照自己控制的節拍來消費數據。批處理系統,如Hadoop或者是一個數據倉庫,或許只是每小時或者每天消費一次數據,而實時查詢系統可能需要及時到秒。由于無論是原始數據源還是日志,都沒有各種目標數據系統的相關知識,因此消費方系統可以被添加和刪除,而無需傳輸管道的變化。
“每個工作數據管道設計得就像是一個日志;每個損壞的數據管道以其自己的方式損壞。”―Count Leo Tolstoy (由作者翻譯)
特別重要的是:目標系統只知道日志,不知道數據源系統的任何細節。消費方系統自身無需考慮數據到底是來自于一個RDBMS(關系型數據庫管理系統Relational Database Management System),一種新型的鍵值存儲,或者它不是由任何形式的實時查詢系統所生成的。這似乎是一個小問題,但實際上是至關重要的。
這里我使用術語“日志”取代了“消息系統”或者“發布―訂閱”,因為它在語義上更明確,并且對支持數據復制的實際實現這樣的需求,有著更接近的描述。我發現“發布訂閱”并不比間接尋址的消息具有更多的含義――如果你比較任何兩個發布―訂閱的消息傳遞系統的話,你會發現他們承諾的是完全不同的東西,而且大多數模型在這一領域都不是有用的。你可以認為日志是一種消息系統,它具有持久性保證和強大的訂閱語義。在分布式系統中,這個通信模型有時有個(有些可怕的)名字叫做原子廣播。
值得強調的是,日志仍然只是基礎設施。這并不是管理數據流這個故事的結束:故事的其余部分圍繞著元數據,模式,兼容性,以及處理數據結構的所有細節及其演化。除非有一種可靠的,一般的方法來處理數據流運作,語義在其中總是次要的細節。
在LinkefIn(SNS社交網站)
在LinkedIn從集中式關系數據庫向分布式系統集合轉化的過程中,我看到這個數據集成問題迅速演變。
現在主要的數據系統包括:
這些都是專門的分布式系統,在其專業領域提供先進的功能。
這種使用日志作為數據流的思想,甚至在我到這里之前就已經與LinkedIn相伴了。我們開發的一個最早的基礎設施之一,是一種稱為databus 的服務,它在我們早期的Oracle表上提供了一種日志緩存抽象,可伸縮訂閱數據庫修改,這樣我們就可以很好支持我們的社交網絡和搜索索引。
我會給出一些歷史并交代一下上下文。我首次參與到這些大約是在2008年左右,在我們轉移鍵值存儲之后。我的下一個項目是讓一個工作中的Hadoop配置演進,并給其增加一些我們的推薦流程。由于缺乏這方面的經驗,我們自然而然的安排了數周計劃在數據的導入導出方面,剩下的時間則用來實現奇妙的預測算法。這樣我們就開始了長途跋涉。
我們本來計劃是僅僅將數據從現存的Oracle數據倉庫中剖離。但是我們首先發現將數據從Oracle中迅速取出是一種黑暗藝術。更糟的是,數據倉庫的處理過程與我們為Hadoop而計劃的批處理生產過程不適合――其大部分處理都是不可逆轉的,并且與即將生成的報告具體相關。最終我們采取的辦法是,避免使用數據倉庫,直接訪問源數據庫和日志文件。最后,我們為了加載數據到鍵值存儲并生成結果,實現了另外一種管道。
這種普通的數據復制最終成為原始開發項目的主要內容之一。糟糕的是,在任何時間任意管道都有一個問題,Hadoop系統很大程度上是無用的――在錯誤的數據基礎上運行奇特的算法,只會產生更多的錯誤數據。
雖然我們已經以一種通用的方式創建事物,但是每個數據源都需要自定義配置安裝。這也被證明是巨量錯誤與失敗的根源。我們在Hadoop上實現的網站功能已經開始流行起來,同時我們發現我們有一長串感興趣的工程師。每個用戶都有他們想要集成的一系列系統,他們想要的一系列新數據源。
古希臘時代的 ETL(提取轉換加載Extract Transform and Load)。并沒有太多變化
有些東西在我面前開始漸漸清晰起來。
首先,我們已建成的通道雖然有一些雜亂,但實質上它們是很有價值的。在采用諸如Hadoop的新的處理系統生成可用數據的過程,它開啟了大量的可能性。 基于這些數據過去很難實現的計算,如今變為可能。 許多新的產品和分析技術都來源于把分片的數據放在一起,這些數據過被鎖定在特定的系統中。
第二, 眾所周知,可靠的數據加載需要數據通道的深度支持。如果我們可以捕獲所有我們需要的結構,我就就可以使得Hadoop數據全自動的加載,這樣就不需要額外的操作來增加新的數據源或者處理模式變更