I ? Logs 第一章 導言

第1章 導言

這是一本關于日志的書。為什么有人會寫這么長的文章來講日志?事實證明,樸素的日志是一種抽象,它是從NoSQL數(shù)據(jù)庫到加密貨幣的各種系統(tǒng)的核心。除了可能偶爾查閱一下日志文件外,大多數(shù)工程師對日志都沒有太深的思考。為了解決這個問題,我會大致地描述日志在分布式系統(tǒng)中的工作方式,然后將這些概念實際應用于一些常見用途:數(shù)據(jù)集成,企業(yè)系統(tǒng)架構,實時數(shù)據(jù)處理以及數(shù)據(jù)系統(tǒng)設計。我還將談論我在LinkedIn工作期間,有關數(shù)據(jù)基礎結構系統(tǒng)的工作中,將其中一些想法付諸實踐的經(jīng)驗。但是在正式開始之前,我還是要解釋一下你可能已經(jīng)了解的內容。

什么是日志?

當大多數(shù)人想到日志時,他們想到的可能會是圖1-1所示的樣子。

圖1-1.Apache日志的摘錄

每個程序員都對這種日志很熟悉——一系列松散的結構請求,錯誤或其他消息(一系列旋轉文本文件)。

這種日志是我將要描述的“日志”概念的退化形式。最大的區(qū)別是這類應用程序日志主要供人類閱讀,而我將介紹的日志同時還需要供程序來訪問。

實際上,如果你細思一下就會發(fā)現(xiàn),由人類在單臺機器上閱讀日志的想法并不好。當涉及許多服務和服務器時,這種方法很快就會變得難以持續(xù)。日志的作用很快會變成根據(jù)輸入查詢或展示圖形,以便了解更多機器上的行為,此時,日志文件中的英文文本并不像我將要討論的那種結構化日志那樣合適。

我想討論的日志是一種更為通用,并且更接近數(shù)據(jù)庫或系統(tǒng)領域中可能稱為“commit log(提交日志)”或“journal(通報)”的概念。它是按時間排序的只可追加的記錄序列,如圖1-2所示。

圖1-2 結構化日志(記錄從0開始編號,根據(jù)它們的寫入順序)

每個矩形代表一個附加到日志的記錄。記錄按其添加順序存儲。讀取從左到右進行。附加到日志的每個條目都分配有一個唯一的順序日志條目編號,該編號是它的唯一鍵。記錄的內容和格式在這里并不重要。具體來說,我們可以想象每個記錄都是一個JSON Blob,當然,任何數(shù)據(jù)格式都可以。

記錄的順序定義了“時間”的概念,因為左側的條目定義為比右側的條目更早。日志條目編號可以認為是條目的“時間戳”。將這種順序描述為時間概念起初似乎有些奇怪,但是它具有與任何特定物理時鐘解耦的便利特性。當我們討論分布式系統(tǒng)時,此特性將變得至關重要。

一個日志與一個文件或一張表并沒有什么不同。文件是字節(jié)數(shù)組,表是記錄數(shù)組,日志實際上就是一種表或文件,只是其中的記錄要按時間排序。

與我之前展示的Apache日志相比,你可以看到:兩者都是僅追加記錄的序列。但是,重要的是我們將日志視為抽象數(shù)據(jù)結構,而不是文本文件。

基于這一點,您可能會想:“這么簡單的事有什么值得談的?” 以何種方式追加記錄序列與數(shù)據(jù)系統(tǒng)有什么關系?答案是日志有特定的用途:它們記錄發(fā)生的情況和時間。對于分布式數(shù)據(jù)系統(tǒng)來說,從很多方面看,這都是問題的核心。

數(shù)據(jù)庫中的日志

我不知道日志概念起源于何處——可能類似于二進制搜索,發(fā)明它的人可能會覺得太過簡單,以至于無法意識到這是一項發(fā)明。它早在IBM的R系統(tǒng)中就已存在。在數(shù)據(jù)庫中的應用與出現(xiàn)崩潰時保持各種數(shù)據(jù)結構和索引的同步有關。為了讓其具有原子性和持久性,數(shù)據(jù)庫會在將一次更改應用到任何數(shù)據(jù)結構之前,使用日志寫出即將修改的記錄的有關信息。日志就是發(fā)生過的事情的記錄,每個表或索引都是這些歷史記錄對數(shù)據(jù)結構以及索引的投射。由于日志會即可生成,因此在發(fā)生崩潰時,它將用作還原所有其他持久性結構的權威來源。

隨著多年的發(fā)展,日志的使用從ACID數(shù)據(jù)庫(atomicity 原子性,consistency一致性 ,isolation隔離性和durability持久性)的特性實現(xiàn)細節(jié)發(fā)展為在數(shù)據(jù)庫之間復制數(shù)據(jù)的方法。事實證明,數(shù)據(jù)庫中發(fā)生的更改順序正是保持遠程副本數(shù)據(jù)庫同步所需的。Oracle,MySQL,PostgreSQL和MongoDB都包含日志傳送協(xié)議,來將日志的一部分傳輸?shù)綇膶俚母北緮?shù)據(jù)庫中。然后,從服務器中將日志所記錄的更改應用于本地數(shù)據(jù)結構,以保持與主服務器同步。Oracle已將日志作為非Oracle數(shù)據(jù)訂閱者的XStreamsGoldenGate產(chǎn)品的通用數(shù)據(jù)訂閱機制,使其產(chǎn)品化,并且MySQL和PostgreSQL中也存在類似的功能。

事實上,本書其余大部分內容中日志的使用方式都是數(shù)據(jù)庫中的兩種用法的變體:

  1. 日志用作發(fā)布/訂閱機制,以將數(shù)據(jù)傳輸?shù)狡渌北?/li>
  2. 日志用作一致性機制,用于訂閱要應用于的更新多個副本

也許是由于起源于數(shù)據(jù)庫內部,機器可讀日志的概念尚未廣為人知,盡管正如我們將看到的那樣,這種抽象是支持各種消息傳遞,數(shù)據(jù)流和實時數(shù)據(jù)處理的理想選擇。

分布式系統(tǒng)日志

對于所有分布式系統(tǒng),最基本的問題之一就是數(shù)據(jù)庫日志解決的相同問題(例如,將數(shù)據(jù)分發(fā)給副本并同意更新順序)。

以日志為中心的分布式系統(tǒng)方法源于一個簡單的觀察,即我稱之為“狀態(tài)機復制”的理論:

如果兩個相同的確定性過程,以相同的狀態(tài)開始,并以相同的順序獲得相同的輸入,則它們將產(chǎn)生相同的輸出并以相同的狀態(tài)結束。

這似乎有點難理解,所以讓我們深入了解它的含義。

確定性 意味著操作不依賴于時序,同時也不允許任何其他影響其結果的輸入存在。例如,以下情況就符合不確定性的模型:一個多線程程序,其輸出取決于線程的執(zhí)行順序;或者一個程序受對gettimeofday()函數(shù)的調用結果影響,或依賴其他不可重復的源輸入。當然,這些東西是否真的是確定性的,更多情況還和物理因素有關。對我們而言沒什么關系,我們對它們的狀態(tài)和輸入不足夠了解,還無法將它們的輸出建模為適當?shù)臄?shù)學函數(shù)。

進程的狀態(tài)指的是操作完成后保留在計算機內存或磁盤中的任何數(shù)據(jù)。

關于以相同順序獲得相同輸入的部分,必須提醒一下——這就是日志的來源。

因此,這實際上是一個非常直觀的概念:如果向兩個確定性代碼段輸入相同的日志,則它們將以相同的順序產(chǎn)生相同的輸出。

分布式計算的應用非常明顯。你可以減少讓所有機器做一樣的事的問題以及實現(xiàn)一致的日志以將輸入提供給這些進程的問題。在這里,日志的作用是從輸入流中杜絕掉所有的不確定性,以確保正在處理此輸入的每個副本都保持同步。

一旦你理解了它,就不會對這個原理感到艱深晦澀:它所表示的僅僅是“確定性的過程是確定的”。不過,我認為它是用于分布式系統(tǒng)設計的更通用的工具之一。

這都不是什么新鮮的技術。如果說分布式計算古老到已經(jīng)具備了比較經(jīng)典的方法,那么這些就是。

但是,這種基本設計模式的含義并未得到廣泛的理解,并且在企業(yè)系統(tǒng)架構的應用中甚至更少。

還有一個好處就是,離散的日志條目號可以充當副本狀態(tài)的時鐘——你可以用一個數(shù)字來描述每個副本的狀態(tài):已處理的最大日志條目的時間戳。同時的兩個副本將處于相同狀態(tài)。因此,此時間戳與日志結合可以唯一地捕獲副本的整個狀態(tài)。這提供了一個離散的,由事件驅動的時間概念,與機器的本地時鐘不同,它可以很容易地在不同機器之間進行比較。

其他以日志為中心的設計

根據(jù)日志中的內容,如何應用此原理有很多變體。例如,我們可以將傳入請求記錄到服務中,并讓每個副本獨立處理這些請求?;蛘撸覀兛梢詣?chuàng)建一個實例來處理請求,并記錄服務響應請求所經(jīng)歷的狀態(tài)變化。從理論上講,我們甚至可以記錄一系列x86機器指令或需要調用的方法名稱和參數(shù)以供每個副本執(zhí)行。只要兩個進程以相同的方式處理這些輸入,這些進程就將在副本之間保持一致。

不同的社區(qū)會以不同的方式描述相似的模式。數(shù)據(jù)庫人員通常區(qū)分為物理日志記錄和邏輯日志記錄。基于物理或基于行的日志記錄意味著記錄已更改的每一行的內容。邏輯或語句記錄則意味著不記錄已更改的行,而是記錄導致行更改的SQL命令(insert,update和delete語句)。

在分布式系統(tǒng)文獻中通常將兩者區(qū)分為操作和副本 。狀態(tài)機模型 通常是指一個Active-Active模型,在該模型中,我們保留傳入請求的日志,每個副本以日志順序處理每個請求。如果稍作改變,選出一個作為主副本,則可被稱為主從備份模型。主副本按請求到達的順序處理請求,并將由于處理請求而發(fā)生的更改記錄到其狀態(tài)中。其他副本則應用主副本的狀態(tài)更改,以便在主副本掛掉時,同步并接管主副本。

如圖1-3所示,在主從備份模型中,選擇了一個主副本來處理所有讀取和寫入操作。每次寫入都會發(fā)布到日志中。從副本通過訂閱日志,將主副本執(zhí)行的更改應用于其本地狀態(tài)。如果主副本發(fā)生故障,則會從從屬副本中選擇一個新的主服務器。在狀態(tài)機復制模型中,所有節(jié)點都是對等的。寫操作首先進入日志,并且所有節(jié)點均按日志確定的順序應用寫操作。

圖1-3 在主備份模型中,選擇一個主節(jié)點來處理所有讀取和寫入。在狀態(tài)機復制模型中,所有節(jié)點都充當對等方。

一個例子

想要了解使用日志構建系統(tǒng)的不同方法,我們先來看一個小問題。假設我們要實現(xiàn)一個復制的算術服務,該服務將維護一組變量(初始值為零),并對這些值應用加,乘,減,除和查詢。我們的服務將響應以下命令:

x? // get the current value of x
x+=5 // add 5 to x
x-=2 // subtract 2 from x
y*=2 // double y

假設該服務作為遠程網(wǎng)絡服務運行,并通過HTTP發(fā)送請求和響應。

如果我們只有一臺服務器,則實現(xiàn)將非常簡單。它可以將變量存儲在內存或磁盤中,并以接收請求的任何順序更新它們。但是,由于只有一臺服務器,因此我們缺乏容錯能力,也沒有辦法擴展服務范圍(假如我們的算術服務很受歡迎)。

我們可以通過添加復制此狀態(tài)和處理邏輯的服務器來解決此問題。但是,這帶來了一個新問題:服務器之間可能不同步。發(fā)生這種情況的方式有很多。例如,服務器可能以不同的順序接收更新命令(并非所有操作都是可交換的),或者有的服務器發(fā)生故障或無響應而錯過更新。

當然,實際上,大多數(shù)人只是將查詢和更新推送到遠程數(shù)據(jù)庫中。這樣做確實可以讓我們的應用程序不發(fā)生問題,但并不能真正解決問題。畢竟,現(xiàn)在我們需要解決數(shù)據(jù)庫中的容錯問題。因此,出于示例的考慮,讓我們直接討論應用程序中日志的使用。

使用日志,有好幾種方法可以解決這個問題。使用狀態(tài)機副本的方法是,首先將要執(zhí)行的操作寫入日志,然后讓每個副本按日志順序應用這些操作。這樣,日志里就要包含一系列命令,例如“ x + = 5”或“ y * = 2”。

主從副本的方法也是可行的。在這種設計中,我們將選擇其中一個副本作為主要副本(也叫領導者或母版)。這個主副本將在本地按順序執(zhí)行它所接收到的所有命令,并且記錄執(zhí)行命令所產(chǎn)生的一系列變量值。在這種設計中,日志僅包含結果變量值,例如“ x = 1”或“ y = 6”,而不包含產(chǎn)生值的原始命令。其余副本將充當備份(也叫跟隨者或從屬);它們訂閱這個日志,并將新的變量值被動地應用于其本地存儲。當主副本宕機時,我們則從其余副本中選擇一個新的主副本。

這個示例清楚說明了為什么排序對于確保副本之間的一致性至關重要:對加法和乘法命令進行重新排序將產(chǎn)生不同的結果,就像對同一變量的兩次更新重新排序一樣。

日志和共識

分布式日志可以看作是模擬共識問題的數(shù)據(jù)結構。畢竟,日志代表的是產(chǎn)生下一個值的一系列決策。你必須瞥一眼看看Paxos系列算法中的日志,盡管日志構建是它們最常見的實際應用。使用Paxos時,通常使用稱為“ multi-paxos”的協(xié)議擴展來完成,該協(xié)議將日志建模為一系列共識問題,每個問題對應一個日志。該日志在ZAB,RAFT和 Viewstamped Replication等其他協(xié)議中更為突出,它們直接對維護分布式一致日志的問題進行建模。

我懷疑,我們過去對這些東西的看法有些偏頗,這可能是由于幾十年來分布式計算理論已經(jīng)趙越了其實際應用。實際上,共識問題有點太簡單了。計算機系統(tǒng)很少需要確定的單個值,它們幾乎總是處理一系列請求。因此,日志不是簡單的單值寄存器而是更自然的抽象。

此外,對算法的關注也掩蓋了系統(tǒng)所需的底層日志抽象。我懷疑我們最終將把重點放在作為商品化構建基塊的日志上,而不管其實現(xiàn)如何,就像我們經(jīng)常談論哈希表一樣,而不必費心去弄清楚是線性探測還是雜項哈希其他一些變體。日志將變成商品化的界面,許多算法和實現(xiàn)相互競爭將提供最佳的可靠性和最優(yōu)的性能。

變更日志101:表和事件是一對

讓我們先回到數(shù)據(jù)庫。變更日志和表格之間有一種奇妙的二重性。日志類似于銀行處理的所有借貸操作列表,而表格則是所有當前帳戶余額。如果你更改了日志,則可以應用這些更改以創(chuàng)建表并捕獲當前狀態(tài)。表格將記錄每個鍵的最新狀態(tài)(以特定的日志時間為準)。從某種意義上說,日志是更基本的數(shù)據(jù)結構:除了創(chuàng)建原始表之外,您還可以對其進行轉換以創(chuàng)建各種派生表。(是的,表可以為非關系人員提供鍵控數(shù)據(jù)存儲。)

這個過程也可以反向進行:如果您對一個表進行了更新,則可以記錄這些更改并發(fā)布所有更新到該表狀態(tài)的更改日志。這個變更日志正是支持“Near-Real-Time(近實時)”副本所需要的。從這個意義上講,您可以將表和事件視為一對:表支持靜態(tài)數(shù)據(jù),日志捕獲更改行為。日志的神奇之處在于,如果它是更改的完整日志,則它不僅可以保存表最終版本的內容,而且還可以重新創(chuàng)建可能已經(jīng)存在的所有其他版本。實際上,它是對表的每個先前狀態(tài)的一種備份。

這可能會使您想起代碼版本控制。源代碼管理與數(shù)據(jù)庫之間有著密切的關系。版本控制解決了與分布式數(shù)據(jù)系統(tǒng)必須解決的非常相似的問題:管理狀態(tài)的分布式并發(fā)更改。版本控制系統(tǒng)通常需要對補丁序列進行建模,實際上就是日志。你可以直接與當前代碼的已檢出快照進行交互,這個快照類似于表。請注意,在版本控制系統(tǒng)中,就像在其他分布式有狀態(tài)系統(tǒng)中一樣,復制是通過日志進行的:更新時,您只需拉下補丁并將其應用于當前快照即可。

最近有人從Datomic,一家銷售日志數(shù)據(jù)庫的公司,看到了其中一些想法。當然,這些概念并不是該系統(tǒng)獨有的,因為它們已經(jīng)成為分布式系統(tǒng)和數(shù)據(jù)庫文獻的一部分已有十多年了。以上這些內容看起來似乎有點偏純理論。不要絕望!我們將快速介紹實用的內容。

后續(xù)內容

在本書的其余部分中,我將讓你嘗到超越分布式系統(tǒng)或抽象的分布式計算模型內部以外的甜頭,包括:

  • 數(shù)據(jù)集成
    • 使組織的所有數(shù)據(jù)可以輕松地在其所有存儲和處理中使用系統(tǒng)。
  • 實時數(shù)據(jù)處理
    • 計算派生數(shù)據(jù)流。
  • 分布式系統(tǒng)設計
    • 以日志為中心的設計如何簡化實際系統(tǒng)。

這些應用都是圍繞著將日志作為獨立服務的思想設計的。

在每種情況下,日志的實用性都來自于日志提供的簡單功能:生成持久的,可重播的歷史記錄。令人驚訝的是,前面提到的這些日志的應用,其核心都是使多臺計算機以確定的方式以自己的速率回放歷史記錄的能力。


章節(jié)列表

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容