日誌: 分散式系統的核心

green_day發表於2019-03-04

什麼是日誌?

日誌就是按照時間順序追加的、完全有序的記錄序列,其實就是一種特殊的檔案格式,檔案是一個位元組陣列,而這裡日誌是一個記錄資料,只是相對於檔案來說,這裡每條記錄都是按照時間的相對順序排列的,可以說日誌是最簡單的一種儲存模型,讀取一般都是從左到右,例如訊息佇列,一般是線性寫入log檔案,消費者順序從offset開始讀取。
由於日誌本身固有的特性,記錄從左向右開始順序插入,也就意味著左邊的記錄相較於右邊的記錄“更老”, 也就是說我們可以不用依賴於系統時鐘,這個特性對於分散式系統來說相當重要。

日誌的應用

日誌在資料庫中的應用

日誌是什麼時候出現已經無從得知,可能是概念上來講太簡單。在資料庫領域中日誌更多的是用於在系統crash的時候同步資料以及索引等,例如MySQL中的redo log,redo log是一種基於磁碟的資料結構,用於在系統掛掉的時候保證資料的正確性、完整性,也叫預寫日誌,例如在一個事物的執行過程中,首先會寫redo log,然後才會應用實際的更改,這樣當系統crash後恢復時就能夠根據redo log進行重放從而恢復資料(在初始化的過程中,這個時候不會還沒有客戶端的連線)。日誌也可以用於資料庫主從之間的同步,因為本質上,資料庫所有的操作記錄都已經寫入到了日誌中,我們只要將日誌同步到slave,並在slave重放就能夠實現主從同步,這裡也可以實現很多其他需要的元件,我們可以通過訂閱redo log 從而拿到資料庫所有的變更,從而實現個性化的業務邏輯,例如審計、快取同步等等。

日誌在分散式系統中的應用

分散式系統服務本質上就是關於狀態的變更,這裡可以理解為狀態機,兩個獨立的程式(不依賴於外部環境,例如系統時鐘、外部介面等)給定一致的輸入將會產生一致的輸出並最終保持一致的狀態,而日誌由於其固有的順序性並不依賴系統時鐘,正好可以用來解決變更有序性的問題。
我們利用這個特性實現解決分散式系統中遇到的很多問題。例如RocketMQ中的備節點,主broker接收客戶端的請求,並記錄日誌,然後實時同步到salve中,slave在本地重放,當master掛掉的時候,slave可以繼續處理請求,例如拒絕寫請求並繼續處理讀請求。日誌中不僅僅可以記錄資料,也可以直接記錄操作,例如SQL語句。

日誌是解決一致性問題的關鍵資料結構,日誌就像是操作序列,每一條記錄代表一條指令,例如應用廣泛的Paxos、Raft協議,都是基於日誌構建起來的一致性協議。

日誌在Message Queue中的應用

日誌可以很方便的用於處理資料之間的流入流出,每一個資料來源都可以產生自己的日誌,這裡資料來源可以來自各個方面,例如某個事件流(頁面點選、快取重新整理提醒、資料庫binlog變更),我們可以將日誌集中儲存到一個叢集中,訂閱者可以根據offset來讀取日誌的每條記錄,根據每條記錄中的資料、操作應用自己的變更。
這裡的日誌可以理解為訊息佇列,訊息佇列可以起到非同步解耦、限流的作用。為什麼說解耦呢?因為對於消費者、生產者來說,兩個角色的職責都很清晰,就負責生產訊息、消費訊息,而不用關心下游、上游是誰,不管是來資料庫的變更日誌、某個事件也好,對於某一方來說我根本不需要關心,我只需要關注自己感興趣的日誌以及日誌中的每條記錄。

我們知道資料庫的QPS是一定的,而上層應用一般可以橫向擴容,這個時候如果到了雙11這種請求突然的場景,資料庫會吃不消,那麼我們就可以引入訊息佇列,將每個隊資料庫的操作寫到日誌中,由另外一個應用專門負責消費這些日誌記錄並應用到資料庫中,而且就算資料庫掛了,當恢復的時候也可以從上次訊息的位置繼續處理(RocketMQ和Kafka都支援Exactly Once語義),這裡即使生產者的速度異於消費者的速度也不會有影響,日誌在這裡起到了緩衝的作用,它可以將所有的記錄儲存到日誌中,並定時同步到slave節點,這樣訊息的積壓能力能夠得到很好的提升,因為寫日誌都是有master節點處理,讀請求這裡分為兩種,一種是tail-read,就是說消費速度能夠跟得上寫入速度的,這種讀可以直接走快取,而另一種也就是落後於寫入請求的消費者,這種可以從slave節點讀取,這樣通過IO隔離以及作業系統自帶的一些檔案策略,例如pagecache、快取預讀等,效能可以得到很大的提升。

分散式系統中可橫向擴充套件是一個相當重要的特性,加機器能解決的問題都不是問題。那麼如何實現一個能夠實現橫向擴充套件的訊息佇列呢? 假如我們有一個單機的訊息佇列,隨著topic數目的上升,IO、CPU、頻寬等都會逐漸成為瓶頸,效能會慢慢下降,那麼這裡如何進行效能優化呢?

1.topic/日誌分片,本質上topic寫入的訊息就是日誌的記錄,那麼隨著寫入的數量越多,單機會慢慢的成為瓶頸,這個時候我們可以將單個topic分為多個子topic,並將每個topic分配到不同的機器上,通過這種方式,對於那些訊息量極大的topic就可以通過加機器解決,而對於一些訊息量較少的可以分到到同一臺機器或不進行分割槽

2.group commit,例如Kafka的producer客戶端,寫入訊息的時候,是先寫入一個本地記憶體佇列,然後將訊息按照每個分割槽、節點彙總,進行批量提交,對於伺服器端或者broker端,也可以利用這種方式,先寫入pagecache,再定時刷盤,刷盤的方式可以根據業務決定,例如金融業務可能會採取同步刷盤的方式。

3.規避無用的資料拷貝

4.IO隔離

結語

日誌在分散式系統中扮演了很重要的角色,是理解分散式系統各個元件的關鍵,隨著理解的深入,我們發現很多分散式中介軟體都是基於日誌進行構建的,例如Zookeeper、HDFS、Kafka、RocketMQ、Google Spanner等等,甚至於資料庫,例如Redis、MySQL等等,其master-slave都是基於日誌同步的方式,依賴共享的日誌系統,我們可以實現很多系統: 節點間資料同步、併發更新資料順序問題(一致性問題)、永續性(系統crash時能夠通過其他節點繼續提供服務)、分散式鎖服務等等,相信慢慢的通過實踐、以及大量的論文閱讀之後,一定會有更深層次的理解。

相關文章