分布式的系統核心是什麼——日誌

什麼是日誌?

日誌就是按照時間順序追加的、完全有序的記錄序列,其實就是一種特殊的文件格式,文件是一個字節數組,而這裡日誌是一個記錄數據,只是相對於文件來說,這裡每條記錄都是按照時間的相對順序排列的,可以說日誌是最簡單的一種存儲模型,讀取一般都是從左到右,例如消息隊列,一般是線性寫入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時能夠通過其他節點繼續提供服務)、分佈式鎖服務等等,相信慢慢的通過實踐、以及大量的論文閱讀之後,一定會有更深層次的理解。


分享到:


相關文章: