由於日誌本身固有的特性,記錄從左向右開始順序插入,也就意味著左邊的記錄相較於右邊的記錄“更老”, 也就是說我們可以不用依賴於系統時鐘,這個特性對於分佈式系統來說相當重要。
日誌是何時出現已經無從得知,可能是概念上來講太簡單。在資料庫領域中日誌更多的是用於在系統crash的時候同步資料以及索引等,例如MySQL中的redo log,redo log是一種基於磁碟的資料結構,用於在系統掛掉的時候保證數據的正確性、完整性,也叫預寫日誌,例如在一個事物的執行過程中,首先會寫redo log,然後才會應用實際的更改,這樣當系統crash後恢復時就能夠根據redo log進行重放從而恢復資料(在初始化的過程中,這個時候不會還沒有客戶端的連線)。日誌也可以用於資料庫主從之間的同步,因為本質上,資料庫所有的操作記錄都已經寫入到了日誌中,我們只要將日誌同步到slave,並在slave重播就能夠實現主從同步,這裡也可以實現許多其他需要的元件,我們可以透過訂閱redo log 從而拿到資料庫所有的變更,從而實現個人化的業務邏輯,例如審計、快取同步等等。
分散式系統服務本質上就是關於狀態的變更,這裡可以理解為狀態機,兩個獨立的進程(不依賴於外部環境,例如係統時脈、外部介面等)給定一致的輸入將會產生一致的輸出並最終保持一致的狀態,而日誌由於其固有的順序性並不依賴系統時鐘,正好可以用來解決變更有序性的問題。
我們利用這個特性來實現解決分散式系統中遇到的許多問題。例如RocketMQ中的備節點,主broker接收客戶端的請求,並記錄日誌,然後即時同步到salve中,slave在本地重播,當master掛掉的時候,slave可以繼續處理請求,例如拒絕寫入請求並繼續處理讀取請求。日誌中不只可以記錄數據,也可以直接記錄操作,例如SQL語句。
日誌是解決一致性問題的關鍵資料結構,日誌就像是操作序列,每一條記錄代表一條指令,例如應用廣泛的Paxos、Raft協議,都是基於日誌建構的一致性協議。
日誌可以很方便的用於處理資料之間的流入流出,每一個資料來源都可以產生自己的日誌,這裡資料來源可以來自各個方面,例如某個事件流(頁面點擊、快取刷新提醒、資料庫binlog變更),我們可以將日誌集中儲存到一個叢集中,訂閱者可以根據offset來讀取日誌的每筆記錄,根據每筆記錄中的資料、操作套用自己的變更。
這裡的日誌可以理解為訊息佇列,訊息佇列可以起到非同步解耦、限流的作用。為什麼要說解耦呢?因為對消費者、生產者來說,兩個角色的職責都很清晰,就負責生產訊息、消費訊息,而不用關心下游、上游是誰,不管是來資料庫的變更日誌、某個事件也好,對於某一方來說我根本不需要關心,我只需要專注於自己感興趣的日誌以及日誌中的每筆記錄。
我們知道資料庫的QPS是一定的,而上層應用一般可以橫向擴容,這個時候如果到了雙11這種請求突然的場景,數據庫會吃不消,那麼我們就可以引入消息隊列,將每個隊數據庫的操作寫到日誌中,由另外一個應用程式專門負責消費這些日誌記錄並應用到資料庫中,而且就算資料庫掛了,當恢復的時候也可以從上次訊息的位置繼續處理(RocketMQ和Kafka都支援Exactly Once語義),這裡即使生產者的速度異於消費者的速度也不會有影響,日誌在這裡起到了緩衝的作用,它可以將所有的記錄存儲到日誌中,並定時同步到slave節點,這樣消息的積壓能力能夠得到很好的提升,因為寫日誌都是有master節點處理,讀請求這裡分為兩種,一種是tail-read,就是說消費速度能夠跟得上寫入速度的,這種讀可以直接走緩存,而另一種也就是落後於寫入請求的消費者,這種可以從slave節點讀取,這樣透過IO隔離以及作業系統自帶的一些檔案策略,例如pagecache、快取預讀等,性能可以得到很大的提升。
分散式系統中可橫向擴展是一個相當重要的特性,加機器能解決的問題都不是問題。那麼如何實現一個能夠實現橫向擴展的消息隊列呢? 假如我們有一個單機的消息隊列,隨著topic數目的上升,IO、CPU、頻寬等都會逐漸成為瓶頸,性能會慢慢下降,那麼這裡如何進行性能優化呢?
日誌在分散式系統中扮演了很重要的角色,是理解分散式系統各個組件的關鍵,隨著理解的深入,我們發現許多分散式中間件都是基於日誌進行構建的,例如Zookeeper、HDFS、 Kafka、RocketMQ、Google Spanner等等,甚至於資料庫,例如Redis、MySQL等等,其master-slave都是基於日誌同步的方式,依賴共享的日誌系統,我們可以實現許多系統: 節點間資料同步、並發更新資料順序問題(一致性問題)、持久性(系統crash時能夠透過其他節點繼續提供服務)、分散式鎖服務等等,相信慢慢的通過實踐、以及大量的論文閱讀之後,一定會有更深層次的理解。
以上是Linux下高效的日誌庫的應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!