微服務:如何確定資料一致性?

YC
程式愛好者
Published in
5 min readApr 17, 2022

不知道大家有沒有想過,這麼多層的微服務當中,資料都是往一個或多個的在傳,我們該怎麼確保資料的一致性?
這會是一個相當複雜的問題,這篇我會分別介紹三種模式,並引用 ACID 與 CAP,對這方面不夠了解的朋友可以先了解一下喔!

接下來我要分別介紹 Two-Phase Commit Pattern、Saga Pattern 與 Eventual Consistency Pattern。這三個在分散式服務中較常見的模式,來看看哪一種最能在微服務中確保資料傳遞時的一致性。

Two-Phase Commit Pattern

Transaction Manager 會處理分散式交易,在準備交易時,它會跟相應的微服務做溝通,叫它們做好要交易的準備,這是 Prepare Phase。然後會進入 Voting Phase,這時 Transaction Manager 會對服務提出決議,看看現在是否適合 commit,若所有相應的服務都投出 Yes,則會開始進行 commit 指令。但如果有其中一個服務投出 No,這時 Transaction Manager 會因為有某組服務的失敗,如斷線、沒有資源、無法寫入等,提出 Rollback 指令,讓所有資料變回未寫入前。

聽之下,Two-Phase Commit Pattern 的做法好像滿好的,能有效保證資料一致性,但是。。。( 嗯,一定要說這個但是的,不然文章寫不下去呀 ),我們會遇到一點問題,如下:

  1. 過份依賴 Transaction Manager
    如果今天掛掉的是 Transaction Manager,應該就差不多可以收工了。
  2. 沒得到 Response 的困境
    如果有任意服務一直沒有在投票階段作出回應,那這段交易就會一直等待,直至 Timeout,嚴重影響效能。
  3. 投票成功後 commit 失敗
    若投票階段是通過的,但在 commit 時有任一服務失敗了,這會造成「假成功」的問題,若想解決這問題我們還需要另一套工具來監控失敗。
  4. 資源大量被鎖
    因為在做兩階段時,服務的資料都需要被鎖定以等待 commit,如果有多個請求在不斷出現,大量資源就會被鎖定,造成更多的等待。
  5. Scaling 問題
    因為要依賴於 Transaction Manager,而 Transaction Manager 是有狀態 ( Stated ) ,因為需要記住現在的 Phase,所以我們無法輕易的水平擴充。這也違反了微服務該有的特性!
  6. Throughput 降低
    當流量都要等待對應的服務寫入,throughput 要通過 Transaction Manager,這樣自然會失去應對大流量的能力,所以只適合在小規模的服務上。

Saga Pattern

Saga 這機制中有兩個重要的元件,分別是 Sage Log 與 SEC ( Saga Execution Coordinator)。Saga 會將一個交易拆分為數個子交易,並透過 Saga Log 對這些請求進行追蹤。而 SEC 則是主要處理 Saga 的元件,它會接收 Saga 送出的請求,並讀取及更新 Saga Log。SEC 會對剛剛送出的請求進行等待回應,並對失敗的回應進行處理。

在上圖中,在拿到 R2 時,SEC 把 R2送到 service 2,然後 service 2 回傳出 R2 failed 的回應。這時 SEC在拿到 R2 failed 時,它會馬上開始發送一個補償請求 ( Compensating Request ),而補償請求是為了發送還原操作通知給所有已完成操作的服務,也就是 Roll Back,以防止在某操作失敗後的資料不一致問題。另外,在收到某一服務錯誤時,之後的操作就會中斷並開始進行補償請求。

Saga Log 可以是一個 Database,然後 SEC 透過對 DB 查詢或 DB 推送來獲得新的請求。 SEC 對 Service 的請求都可以透過簡單的 REST API 來實現,對於一般的 Saga 請求 SEC 應只發送一次,但對補償請求則可以發送多次,以確保還原成功。

Eventual Consistency Pattern

Eventual Consistency Pattern 的精神在於「資料會在最終變得一致,但不見得要馬上一致」。這樣的設計方式是基於 BASE 交易模型,不同於 ACID 模型著重於提供一個一致性系統,BASE 模型著重於高可用性。這表示了交易的更新是在背景中完成,而不是在交易時立即完成。而這種做法讓效能得到提升,因為資源不會因為交易被大量鎖住,但是也無法立即響應更新。

Eventual Consistency Pattern 很適合用於 long running、在背景執行的任務,對於 Race Conditions 的管理也變得相對簡單。在微服務的世界,我們常常會使用 Event Based 來做交易/行動,而事件模式正正與 Eventual Consistency Pattern 相輔相成,在事件被送出後就不會再被理會,一切都在背景中執行,而在非同步設計中,一致性也不是最重要的事情!

如果你覺得我的文章幫助到你,希望你也可以為文章拍手,分別 Follow 我的個人頁與程式愛好者,按讚我們的粉絲頁喔,支持我們推出更多更好的內容創作!

--

--

YC
程式愛好者

提供更精確的技術內容為目標,另創立「程式愛好者」專頁。首席軟體工程師,專研後端技術、物件導向、軟體架構。