我在六年前的一個令人興奮的時刻加入到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的工作。
為了了解兩種方式的不同,我們來看一個不太嚴謹的例子。假定有一個算法服務的副本,保持一個獨立的數字作為它的狀态(初始值為0),并對這個值進行加法和乘法運算。主動-主動方式應該會輸出所進行的變換,比如“+1”,“*2”等。每一個副本都會應用這些變換,進而得到同樣的解集。主動-被動方式将會有一個獨立的主體執行這些變換并輸出結果日志,比如“1”,“3”,“6”等。這個例子也清楚的展示了為什麼說順序是保證各副本間一緻性的關鍵:一次加法和乘法的順序的改變将會導緻不同的結果。
分布式日志可以了解為一緻性問題模型的資料結構。因為日志代表了後續追加值的一系列決策。你需要重新審視paxos算法簇,盡管日志子產品是他們最常見的應用。 在paxos算法中,它通常通過使用稱之為多paxos的協定,這種協定将日志模組化為一系列的問題,在日志中每個問題都有對應的部分。在zab, raft等其它的協定中,日志的作用尤為突出,它直接對維護分布式的、一緻性的日志的問題模組化。
我懷疑的是,我們就曆史發展的觀點是有偏差的,可能是由于過去的幾十年中,分布式計算的理論遠超過了其實際應用。在現實中,共識的問題是有點太簡單了。計算機系統很少需要決定單個值,他們幾乎總是處理成序列的請求。這樣的記錄,而不是一個簡單的單值寄存器,自然是更加抽象。
此外,專注于算法掩蓋了 抽象系統需要的底層的日志。我懷疑,我們最終會把日志中更注重作為一個商品化的基石,不論其是否以同樣的方式 實施的,我們經常談論一個哈希表而不是糾結我們 得到是不是具體某個細節的哈希表,例如線性或者帶有什麼什麼其它變體哈希表。日志将成為一種大衆化的接口,為大多數算法和其實作提升提供最好的保證和最佳的性能。
變更日志101: 表與事件的二相性。
讓我們繼續聊資料庫。資料庫中存在着大量變更日志和表之間的二相性。這些日志有點類似借貸清單和銀行的流程,資料庫表就是目前的盈餘表。如果你有大量的變更日志,你就可以使用這些變更用以建立捕獲目前狀态的表。這張表将記錄每個關鍵點(日志中一個特别的時間點)的狀态資訊。這就是為什麼日志是非常基本的資料結構的意義所在:日志可用來建立基本表,也可以用來建立各類衍生表。同時意味着可以存儲非關系型的對象。
這個流程也是可逆的:如果你正在對一張表進行更新,你可以記錄這些變更,并把所有更新的日志釋出到表的狀态資訊中。這些變更日志就是你所需要的支援準實時的克隆。基于此,你就可以清楚的了解表與事件的二相性: 表支援了靜态資料而日志捕獲變更。日志的魅力就在于它是變更的完整記錄,它不僅僅捕獲了表的最終版本的内容,它還記錄了曾經存在過的其它版本的資訊。日志實質上是表曆史狀态的一系列備份。
這可能會引起你對源代碼的版本管理。源代碼管理和資料庫之間有密切關系。版本管了解決了一個大家非常熟悉的問題,那就是什麼是分布式資料系統需要解決的— 時時刻刻在變化着的分布式管理。版本管理系統通常以更新檔的釋出為基礎,這實際上可能是一個日志。您可以直接對目前 類似于表中的代碼做出“快照”互動。你會注意到, 與其他分布式狀态化系統類似,版本控制系統 當你更新時會複制日志,你希望的隻是更新更新檔并将它們應用到你的目前快照中。
最近,有些人從datomic –一家銷售日志資料庫的公司得到了一些想法。這些想法使他們對如何 在他們的系統應用這些想法有了開闊的認識。 當然這些想法不是隻針對這個系統,他們會成為 十多年分布式系統和資料庫文獻的一部分。.
<b>原文釋出時間為:2014-03-19</b>
<b></b>
<b>本文來自雲栖社群合作夥伴“大資料文摘”,了解相關資訊可以關注“bigdatadigest”微信公衆号</b>