雙重寫入:如何解決微服務分散式系統中資料不一致? - Thorben

banq發表於2020-02-02

由於許多新應用程式是作為微服務系統構建的,因此雙重寫入已成為一個普遍的問題。它們是導致資料不一致的最常見原因之一。更糟的是,許多開發人員甚至都不知道雙重寫入是什麼。

什麼是雙重寫入?

雙重寫入描述了您在兩個個系統(例如資料庫和Apache Kafka)中更改資料時的情況,而沒有額外的層來確保兩個服務上的資料一致性。

雙重寫入:如何解決微服務分散式系統中資料不一致? - Thorben

只要兩個操作都成功,一切就OK了。即使第一筆交易失敗,也還是可以的。但是,如果您成功提交了第一筆交易而第二筆交易失敗,則說明您遇到了問題。您的系統現在處於不一致狀態,沒有容易修復的方法。

分散式事務不再是一種選擇

過去,當我們構建整體/.單體/Monolith時,我們使用分散式事務來避免這種情況。分散式事務使用兩階段提交協議。它將事務的提交過程分為兩個步驟,並確保所有系統的ACID原則。

但是,如果我們要構建微服務系統,則不會使用分散式事務。這些交易需要鎖,並且無法很好地擴充套件。他們還需要所有涉及的系統同時啟動和執行。

那你該怎麼辦呢?

3個無效的“解決方案”

當我在會議演講中或在一個研討會上與與會者討論此主題時,我經常聽到以下3條建議之一:

  1. 是的,我們知道此問題,我們沒有解決方案。但這還不錯。到目前為止,什麼都沒有發生。讓我們保持原樣。
  2. 讓我們將與Apache Kafka的互動移動到提交後監聽器。
  3. 在提交資料庫事務之前,讓我們將事件寫入Kafka中的主題。

好吧,很明顯,建議1的風險很大。它可能在大多數時間都有效。但是遲早,您將在服務儲存的資料之間建立越來越多的不一致之處。

因此,讓我們集中討論選項2和3。

2. 在提交後監聽器中釋出事件

在提交後監聽器中釋出事件是一種非常流行的方法。它確保僅在資料庫事務成功時才釋出事件。但是,很難解決Kafka崩潰或任何其他原因阻止您釋出事件的情況。

您已經提交了資料庫事務。因此,您無法輕鬆地還原這些更改。當您嘗試在Kafka中釋出事件時,其他事務可能已經使用並修改了該資料。

您可能會嘗試將故障保留在資料庫中,並執行常規的清理作業以嘗試恢復失敗的事件。這可能看起來像是一個合理的解決方案,但是它有一些缺陷:

  1. 僅當您可以將失敗的事件保留在資料庫中時,它才有效。如果資料庫事務失敗,或者您的應用程式或資料庫崩潰,然後才能儲存有關失敗事件的資訊,則將丟失它。
  2. 僅當事件本身沒有引起問題時,它才起作用。
  3. 如果在清除作業恢復失敗的事件之前,另一個操作為該業務物件建立了一個事件,則事件將混亂。

這些似乎是假設的情況,但這就是我們正在準備的事情。本地事務,分散式事務和確保最終一致性的方法的主要思想是,絕對要確保您不會造成任何(永久)不一致。

提交後偵聽器無法確保這一點。因此,讓我們看一下其他選項。

3. 在提交資料庫事務之前釋出事件

在討論了為何提交後監聽器不起作用之後,通常會建議使用這種方法。如果在提交之後釋出事件會導致問題,您只需在提交事務之前釋出它,對嗎?

好吧,不……讓我解釋一下……

如果您無法釋出事件,則在提交事務之前釋出事件使您可以回滾事務。那就對了。

但是,如果資料庫事務失敗,該怎麼辦?

您的操作可能違反唯一約束,或者同一資料庫記錄上可能有2個併發更新。提交期間將檢查所有資料庫約束,並且不能確保它們都不會失敗。資料庫事務也彼此隔離,因此如果不使用鎖,就無法阻止併發更新。但這帶來了新的可伸縮性問題。簡而言之,您的資料庫事務可能會失敗,並且您無能為力,或者對此無能為力。

如果發生這種情況,則您的活動已經發布。其他微服務可能已經觀察到它並觸發了一些業務邏輯。您無法撤回活動。

如前所述,撤消操作失敗的原因相同。您也許可以構建一個大多數情況下都可以使用的解決方案。但是您無法建立絕對安全的東西。

如何避免雙重寫入?

您可以選擇幾種方法來避免雙重寫入。但是您需要知道,如果不使用分散式事務,則只能構建最終一致的系統。

總體思路是將流程分為多個步驟。這些步驟中的每個步驟僅適用於一個資料儲存,例如資料庫或Apache Kafka。這使您能夠使用本地事務,所涉及系統之間的非同步通訊以及非同步的,可能無限的重試機制。

如果您只想在服務之間複製資料或通知其他服務已發生事件,則可以將發件箱模式與Debezium等變更資料捕獲實現一起使用。我在以下文章中詳細解釋了這種方法:

而且,如果您需要實施涉及多個服務的一致的寫入操作,則可以使用SAGA模式。我將在以下文章之一中對其進行詳細說明。

結論

雙重寫入常常被低估,許多開發人員甚至都不知道潛在的資料不一致。

如本文所述,在沒有分散式事務或確保最終一致性的演算法的情況下,寫入2個或更多系統可能會導致資料不一致。如果使用多個本地事務,則無法處理所有錯誤情況。

避免這種情況的唯一方法是將通訊分為多個步驟,並且在每個步驟中僅寫入一個外部系統。SAGA模式和變更資料捕獲實現(例如Debezium)使用這種方法來確保對多個系統的一致寫入操作或將事件傳送到Apache Kafka。

 

相關文章