Netflix計劃於2020年開源的資料庫資料複製重器:DBLog,一個類似Oracle OGG的通用的變更資料捕獲CDC框架 - Netflix

banq發表於2019-12-21

計劃於2020年開源的DBLog的早期研究,其目標使異構資料儲存保持同步。
變更資料捕獲(CDC)允許從資料庫中實時捕獲已提交的變更,並將這些變更傳播給下游使用者,CDC在需要保持多個異構資料儲存同步(例如MySQL和ElasticSearch之間資料同步)並解決傳統技術(如雙重寫入和分散式事務)的挑戰的用例中變得越來越流行。
在MySQL和PostgreSQL等資料庫中,事務日誌是CDC事件源。由於事務日誌的保留期限通常有限,因此不能保證它們包含完整的更改歷史記錄。因此,需要轉儲以捕獲事件源的完整狀態。有幾個開源CDC專案,通常使用相同的基礎庫,資料庫API和協議。
但是,我們發現了許多無法滿足我們要求的限制,例如,在轉儲完成之前暫停日誌事件的處理,缺少按需觸發轉儲的能力或透過使用表鎖來阻止寫入流量的實現。這激發了DBLog的開發,後者在通用框架下提供日誌和轉儲處理。為了獲得支援,需要資料庫來實現一組功能,這些功能在MySQL,PostgreSQL,MariaDB等系統中通常可用。
DBLog的某些功能包括:
  • 按順序處理捕獲的日誌事件。
  • 可以隨時在所有表之間針對特定表或特定表的主鍵進行轉儲。
  • 透過將轉儲分塊進行交織並記錄轉儲事件。這樣日誌處理可以與轉儲處理一起進行。如果處理過程終止,則可以在最後完成的塊之後繼續執行,而無需從頭開始。如果需要,這還可以限制和暫停轉儲。
  • 永遠不會獲得表上的鎖,這可以防止影響源資料庫上的寫流量。
  • 支援任何型別的輸出,因此輸出可以是流,資料儲存甚至是API。
  • 設計時要考慮高可用性。因此,下游消費者有信心在源頭上發生變化事件。


需求
在先前的部落格文章中,我們討論了資料豐富和同步平臺Delta。Delta的目標是使多個資料儲存保持同步,其中一個儲存是事實的來源(如MySQL),而其他則是派生的儲存(如ElasticSearch)。關鍵要求之一是從真相源到目的地的傳播延遲低,並且事件流高度可用。無論同一團隊使用多個資料儲存,還是一個團隊擁有另一個團隊正在使用的資料,這些條件都適用。在Delta部落格文章中,我們還描述了資料同步之外的用例,例如事件處理。
對於資料同步和事件處理用例,除了實時捕獲更改之外,我們還需要滿足以下要求:

  • 捕獲完整狀態。派生儲存區(例如ElasticSearch)最終必須儲存源的完整狀態。我們透過源資料庫中的轉儲提供此功能。
  • 隨時觸發維修。我們的目標不是在任何時候都將轉儲視為一次性設定活動,而是旨在在任何時候啟用它們:跨所有表,特定表或特定主鍵。當資料丟失或損壞時,這對於下游修復至關重要。
  • 為實時事件提供高可用性。實時更改的傳播對可用性有很高的要求;如果事件流停止較長時間段(例如幾分鐘或更長時間),則這是不希望的。即使在進行維修時也必須滿足此要求,以使它們不會使實時事件停頓。我們希望將實時事件和轉儲事件交織在一起,以便兩者都能取得進展。
  • 最小化資料庫影響。連線到資料庫時,重要的是要確保其頻寬和為應用程式提供讀寫服務的能力受到的影響儘可能小。因此,最好避免使用會阻塞寫流量(例如表上的鎖)的API。除此之外,還必須放置控制元件以允許限制日誌和轉儲處理,或者在需要時暫停處理。
  • 將事件寫入任何輸出。對於流技術,Netflix利用了多種選擇,例如Kafka,SQS,Kinesis,甚至是Netflix特定的流解決方案(例如Keystone)。儘管將流作為輸出可能是一個不錯的選擇(例如有多個使用方時),但這並不總是一個理想的選擇(好像只有一個使用方)。我們希望提供直接寫入目標而不透過流的功能。目的地可以是資料儲存或外部API。
  • 支援關聯式資料庫。Netflix上有一些服務透過AWS RDS使用RDBMS型別的資料庫,例如MySQL或PostgreSQL。我們希望支援這些系統作為源,以便它們可以提供其資料以供進一步使用。


現有解決方案
我們評估了一系列現有的開源產品,包括:MaxwellSpinalTap,Yelp的MySQL StreamerDebezium。現有的解決方案在捕獲源自事務日誌的實時更改方面相似。例如,透過使用MySQL的binlog複製協議或PostgreSQL的複製插槽。
在轉儲處理方面,我們發現現有解決方案至少具有以下限制之一:

  • 在處理轉儲時停止日誌事件處理。如果在進行轉儲時未處理日誌事件,則適用此限制。因此,如果轉儲的容量很大,日誌事件處理將停滯較長時間。當下遊使用者依賴實時更改的短傳播延遲時,這是一個問題。
  • 缺少按需觸發轉儲的能力。大多數解決方案最初都是在引導階段或在事務日誌中檢測到資料丟失時執行轉儲。但是,按需觸發轉儲的功能對於引導下游的新使用者(例如新的ElasticSearch索引)或資料丟失時的修復至關重要。
  • 透過鎖定表來阻止寫流量。一些解決方案使用表上的鎖來協調轉儲處理。根據實現和資料庫的不同,鎖定的時間可以很短,也可以在整個轉儲過程中持續[5]。在後一種情況下,寫流量將被阻止,直到轉儲完成。在某些情況下,可以配置專用的只讀副本,以避免影響主伺服器上的寫入。但是,此策略不適用於所有資料庫。例如,在PostgreSQL RDS中,更改只能從主資料庫中捕獲。
  • 使用專有的資料庫功能。我們發現某些解決方案使用了不可轉移到其他系統的高階資料庫功能,例如:使用MySQL的黑洞引擎或從建立PostgreSQL複製插槽中獲取轉儲的一致快照。這樣可以防止跨資料庫的程式碼重用。

最終,我們決定採用其他方法來處理轉儲。其中之一:
  • 交織日誌與轉儲事件,以便兩者都能取得進展
  • 允許隨時觸發轉儲
  • 不使用表鎖
  • 使用標準化的資料庫功能


DBLog框架​​​​​​​
DBLog是基於Java的框架,能夠實時捕獲更改並進行轉儲。轉儲是按塊進行的,因此它們與實時事件交織,並且不會長時間停止實時事件處理。可以隨時透過提供的API進行轉儲。這允許下游使用者最初或在以後的時間捕獲完整的資料庫狀態以進行修復。
我們設計了框架以最大程度地減少資料庫影響。轉儲可以根據需要暫停和恢復。這與失敗後的恢復以及在資料庫達到瓶頸時停止處理有關。我們也不對錶進行鎖定,以免影響應用程式的寫入。
DBLog允許將捕獲的事件寫入任何輸出,即使它是另一個資料庫或API。我們使用Zookeeper來儲存與日誌和轉儲處理相關的狀態,並用於組長選舉。我們在構建DBLog時考慮到了可插入性,允許根據需要交換實現(例如,用其他東西代替Zookeeper)。
以下小節將更詳細地說明日誌和轉儲處理。

日誌處理
該框架要求資料庫實時為每個更改的行提交事件。假定事務日誌是這些事件的起源。資料庫正在將它們傳送到DBLog可以使用的傳輸方式。對於該傳輸,我們使用術語“ 更改日誌”。事件的型別可以是:create,update或delete。對於每個事件,需要提供以下內容:日誌序列號,操作時的列狀態以及操作時應用的架構
每個更改都被序列化為DBLog事件格式,併傳送給寫入器,以便可以將其傳遞到輸出。向寫入器傳送事件是非阻塞操作,因為寫入器程式在其自己的執行緒中執行,並在內部緩衝區中收集事件。緩衝的事件按順序寫入輸出。該框架允許插入自定義格式化程式,以將事件序列化為自定義格式。輸出是一個簡單的介面,允許插入任何所需的目標,例如流,資料儲存甚至API。

轉儲處理
由於轉儲日誌的事件保留時間有限,因此需要重新轉儲,這會阻止轉儲用於重構完整的源資料集。轉儲事件以大塊形式進行,以便它們可以與日誌事件交錯,從而允許兩者都進行。將為塊的每個選定行生成一個事件,並以與日誌事件相同的格式對其進行序列化。這樣,如果事件源自日誌或轉儲,則無需擔心下游使用者。日誌和轉儲事件都透過同一寫入器傳送到輸出。
可以隨時透過API為所有表,特定表或特定表主鍵安排轉儲。每個表的轉儲請求以已配置大小的塊執行。另外,可以配置一個延遲來阻止對新塊的處理,從而僅允許在這段時間內進行日誌事件處理。塊大小和延遲允許在日誌和轉儲事件處理之間取得平衡,並且可以在執行時更新這兩個設定。
透過按升序對主表進行排序幷包括行的方式來選擇塊,其中主鍵大於上一個塊的最後一個主鍵。資料庫需要有效地執行此查詢,這通常適用於對主鍵執行範圍掃描的系統。
需要採用這樣的方式進行分塊處理:長時間不停止日誌事件處理,並保留日誌更改的歷史記錄,以便具有較早值的選定行無法覆蓋日誌事件中的較新狀態。
為了實現這一點,我們在更改日誌中建立了可識別的水印事件,以便可以對塊選擇進行排序。水印是透過源資料庫中的表實現的。該表儲存在專用名稱空間中,因此不會與應用程式表發生衝突。儲存UUID欄位的表中僅包含一行。透過將該行更新為特定的UUID來生成水印。行更新導致更改事件,該事件最終透過更改日誌接收。
透過使用水印,將使用以下步驟進行轉儲:

  1. 短暫暫停日誌事件處理。
  2. 透過更新水印表來生成低水印。
  3. 對下一個塊執行SELECT語句,並將結果集儲存在記憶體中,並按主鍵索引。
  4. 透過更新水印表來生成高水印。
  5. 恢復將接收到的日誌事件傳送到輸出。監視日誌中的高低水印事件。
  6. 收到低水印事件後,開始從低水印之後接收的所有日誌事件主鍵的結果集中刪除條目。
  7. 收到高水位標記事件後,在處理新的日誌事件之前,將所有剩餘的結果集條目傳送到輸出。
  8. 如果存在更多塊,請轉到步驟1。

假定SELECT從一致的快照返回狀態,該快照表示直到歷史記錄特定點的已提交更改。或等效地:SELECT在更改日誌的特定位置執行,考慮到該點為止的更改。資料庫通常不公開與select語句執行相對應的日誌位置(MariaDB是一個例外)。
我們方法的核心思想是確定更改日誌上的一個視窗,該視窗保證包含SELECT。由於確切的選擇位置未知,因此將刪除所有選擇的行,這些行與該視窗內的日誌事件發生衝突。這確保了塊選擇不會覆蓋日誌更改的歷史記錄。透過寫低水印開啟視窗,然後執行選擇,最後,透過寫高水印關閉視窗。為了使它起作用,SELECT 必須從低水位標記時間或更晚的時間開始讀取最新狀態(如果選擇還包括在低水位標記寫入之後且在讀取之前提交的寫入,則可以)。
...塊演算法點選標題見原文與圖

資料庫支援
為了使用DBLog,資料庫需要根據提交的更改和非失效讀取的線性歷史記錄提供更改日誌。這些條件可以透過MySQL,PostgreSQL,MariaDB等系統來滿足,因此可以在此類資料庫中統一使用該框架。
到目前為止,我們新增了對MySQL和PostgreSQL的支援。由於每個資料庫使用專有協議,因此需要使用不同的庫來整合所需的日誌事件。對於MySQL,我們使用shyiko / mysql-binlog-connector來實現binlog複製協議,以便從MySQL主機接收事件。對於PostgreSQL,我們在wal2json外掛中使用了複製插槽。更改是透過PostgreSQL jdbc驅動程式實現的流複製協議接收的。在MySQL和PostgreSQL之間,確定每個捕獲的更改的模式有所不同。在PostgreSQL中,wal2json包含列名稱和型別以及列值。對於MySQL模式,必須跟蹤作為binlog事件接收的更改。
轉儲處理透過使用SQL和JDBC進行整合,僅需要實現塊選擇和水印更新。相同的程式碼用於MySQL和PostgreSQL,也可以用於其他類似的資料庫。轉儲處理本身不依賴SQL或JDBC,並且允許整合滿足DBLog框架要求的資料庫,即使它們使用不同的標準也是如此。

高可用性
DBLog使用主-備架構。一個例項是主動的,其他例項是被動的。我們利用Zookeeper進行領導者選舉來確定活動例項。領導權是一種租約,如果沒有及時重新整理,就會丟失,從而允許另一個例項接管。當前,我們每個AZ部署一個例項(通常有3個AZ),因此,如果一個AZ發生故障,則另一個AZ中的一個例項可以以最小的總體停機時間繼續進行處理。跨區域的被動例項也是可能的,儘管建議將其與資料庫主機在同一區域中執行,以保持較低的變更捕獲延遲。

生產用途
DBLog是Netflix的MySQL和PostgreSQL聯結器的基礎,後者在Delta中使用。自2018年以來,Delta已在Netflix Studio應用程式的生產中用於資料儲存同步和事件處理用例。在DBLog之上,Delta聯結器正在使用自定義事件序列化程式,因此在將事件寫入輸出時將使用Delta事件格式。Netflix特定的流用作Keystone等輸出。
除Delta之外,DBLog還用於為其他具有自己的資料格式的Netflix資料移動平臺構建聯結器。

DBLog具有本部落格文章未涵蓋的其他功能,例如:

  • 無需使用鎖即可捕獲表模式的能力。
  • 模式儲存庫整合。儲存傳送到輸出的每個事件的模式,並在每個事件的有效負載中具有對模式儲存的引用。
  • 單調寫入模式。確保一旦為特定行寫入了狀態,之後就不能再寫入較新的狀態。這樣,下游消費者僅在向前的方向上經歷狀態轉換,而不會來回移動。

我們計劃在2020年開源DBLog,幷包括其他文件。




 

相關文章