微服務分散式事務處理

語言: CN / TW / HK

theme: channing-cyan

當我們向微服務架構遷移時,如何處理好分散式事務是必須考慮的問題。這篇文章介紹了分散式事務處理的兩種方案,可以結合實際採用合適的解決方案。原文:Handling Distributed Transactions in the Microservice world[1]

如今每個人(包括我)都在思考、構建微服務,分散式系統是微服務的核心原則和一切實現的上下文。

什麼是分散式事務?

跨越網路上多個物理系統或計算機的事務被簡單的稱為分散式事務。在微服務世界中,事務被分割到多個服務中,需要按順序呼叫這些服務以完成整個事務。

下面是一個單體電子商務系統使用事務的例子:

圖1: 單體中的事務

圖1: 單體中的事務

在上面的系統中,如果使用者向平臺傳送Checkout請求,平臺將建立一個本地資料庫事務,該事務操作多個數據庫表,以處理訂單並從庫存中保留商品。如果有任何步驟失敗,事務(包括訂單和保留的商品)可以回滾。這被稱為ACID(原子性 Atomicity、一致性 Consistency、隔離性 Isolation、永續性 Durability),由資料庫系統保證。

下面是電子商務系統分解為微服務的情況:

圖2: 微服務中的事務

圖2: 微服務中的事務

當我們解耦這個系統時,建立了微服務OrderMicroserviceInventoryMicroservice,各自有獨立的資料庫。當用戶發起Checkout請求時,這兩個微服務都將被呼叫從而將更改應用到各自的資料庫中。因為事務是通過多個系統跨多個數據庫的,所以現在這是一個分散式事務

微服務中的分散式事務有什麼問題?

隨著微服務體系架構的出現,事務可以跨越多個微服務,從而跨越資料庫,因此我們現在無法利用資料庫的ACID特性,從而面臨以下關鍵問題:

如何保持事務的原子性?

原子性意味著事務要麼完成所有步驟,要麼沒有完成任何步驟。在上面的例子中,如果InventoryMicroservice方法中的“保留商品”失敗,如何回滾OrderMicroservice應用的“處理訂單”?

如何處理併發請求?

如果某個微服務的物件被持久化到資料庫中,同時有另一個請求讀取相同的物件。服務應該返回舊資料還是新資料?在上面的例子中,一旦OrderMicroservice已經完成,那麼InventoryMicroservice在執行更新的過程時,客戶下單的請求中應該包括當前的訂單嗎?

如今,系統應該為失敗而設計,其中主要的問題就是處理分散式事務。下面引用Pat Helland的話:

一般來說,應用程式開發人員不會簡單的就能實現支援分散式事務的大型可伸縮應用系統。—— Pat Helland

可能的解決方案

在設計和構建基於微服務的應用時,上述兩個問題非常關鍵。為了解決這些問題,下面列舉幾種方法:

  • 兩階段提交(Two-Phase Commit)
  • 最終一致性和補償(Eventual Consistency and Compensation )/ SAGA
1. 兩階段提交

顧名思義,這種處理事務的方式有兩個階段,準備階段和提交階段,其中起到重要作用的是事務協調器(Transaction Coordinator),負責維護事務的生命週期。

工作方式:

在準備階段,所有涉及到的微服務都準備提交,並通知協調器已經準備好完成事務。然後在提交階段,事務協調器向所有微服務發出提交或回滾命令。

以電子商務系統為例:

圖3: 在微服務上成功的兩階段提交

圖3: 在微服務上成功的兩階段提交

在上面的示例中(圖3),當用戶傳送Checkout請求時,TransactionCoordinator將發起一個帶有所有上下文資訊的全域性事務。首先,向OrderMicroservice傳送prepare命令建立訂單。然後,向InventoryMicroservice傳送prepare命令保留商品。當兩個服務都可以執行更改時,它們將鎖定物件,不再接受其他更改,並通知TransactionCoordinator。一旦TransactionCoordinator確認所有微服務都已準備好應用更改,就會通過請求事務commit來要求這些微服務持久化所作的更改,然後所有物件才能被解鎖。

圖4: 在微服務上失敗的兩階段提交

圖4: 在微服務上失敗的兩階段提交

在失敗的場景中(圖4)——如果在任何時候有某個微服務沒有做好準備,```TransactionCoordinator```將中止事務併發起回滾流程。圖中由於某種原因,```OrderMicroservice```未能建立訂單,但是```InventoryMicroservice```已經回覆說它準備建立訂單。```TransactionCoordinator```將請求```InventoryMicroservice```中止建立訂單,並回滾所做的任何更改、解鎖資料庫物件。 **優點** - 該方法保證事務是原子的。交易結束時,要麼所有微服務都成功,要麼所有微服務都沒有改變。 - 其次,允許讀寫分離,在事務協調器提交更改之前,物件上的更改是不可見的。 - 這種方法通過同步呼叫通知客戶端成功或失敗。 **缺點** - 沒什麼事情是完美的,兩階段提交與單個微服務的處理時間比起來慢很多,並且高度依賴於事務協調器,在高負載期間,事務協調器確實會降低系統的速度。 - 另一個主要缺點是資料庫行鎖定,該鎖可能成為效能瓶頸,並且可能出現兩個事務相互鎖定造成的**死鎖**。 ##### 2. 最終一致性和補償/SAGA 最終一致性的最佳定義之一是microservices.io[2]描述的:*每個服務在更新資料時釋出一個事件。其他服務訂閱事件,當接收到事件時,更新其資料。* 在這種方法中,分散式事務由相關微服務上的非同步本地事務來完成,微服務通過事件匯流排相互通訊。 **工作方式:** 再以電子商務系統為例: ![圖5: 最終的一致性/SAGA,成功的場景](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1f87f393d1643b9b2079105231971dd~tplv-k3u1fbpfcp-zoom-1.image)

圖5: 最終的一致性/SAGA,成功的場景

在上面的例子中(圖5),客戶端請求系統*處理訂單*。在處理過程中,```Choreographer```發出一個*Create Order*事件,表示開始一個事務。```OrderMicroservice```監聽到這個事件並建立一個訂單,如果成功,發出一個*Order Created*事件。```Choreographer```偵聽此事件,並通過發出*Reserve items*事件繼續保留商品。```InventoryMicroservice```偵聽此事件並保留商品,如果成功,發出*Items Reserved*事件。在這個例子中,這意味著事務的結束。 微服務之間所有基於事件的通訊都是通過事件匯流排進行的,並由另一個系統編排以解決複雜性問題。 ![圖6: 最終的一致性/SAGA,失敗場景](http://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d390863a753b4ab28540ab60be91c78b~tplv-k3u1fbpfcp-zoom-1.image)

圖6: 最終的一致性/SAGA,失敗場景

如果由於任何原因```InventoryMicroservice```未能保留商品(圖6),它會發出*Failed to Reserve Items*事件。```Choreographer```偵聽此事件,並通過發出*Delete Order*事件啟動**補償事務**。```OrderMicroservice```偵聽此事件並刪除所建立的訂單。 **優點** 這種方法的一大優點是每個微服務只關注自己的原子事務。如果某個服務花費了更長的時間,其他微服務不會被阻塞,這也意味著不需要資料庫鎖。由於其基於非同步事件的解決方案,這種方法可以使系統在高負載下具有高度的可伸縮性。 **缺點** 該方法的主要缺點是沒有讀取隔離。這意味著在上面的示例中,客戶端可以看到已建立的訂單,但在下一秒中,由於補償事務,訂單會被刪除。此外,當微服務的數量增加時,除錯和維護就變得更加困難。 ### 結論 首先儘量避免分散式事務,如果正在構建新應用,那麼就從單體開始,如Martin Fowler在MonolithFirst[3]中所描述的那樣: >更常見的方法是從單體開始,逐漸剝離邊緣的微服務。這種方法可以在微服務體系架構的核心留下一個巨大的單體,大多數新的開發都發生在微服務中,而這個單體相對來說變化不大。— Martin Fowler 當一個事件需要在兩個地方更新資料時,與兩階段提交相比,最終一致性/SAGA方案是處理分散式事務的更好的方式,主要原因是兩階段提交在分散式環境中不能伸縮。不過最終一致性方案引入了新問題,例如如何以原子方式更新資料庫和發出事件,因此採用這種方案需要開發和測試團隊改變思維方式。 >**References:** \ >[1] Handling Distributed Transactions in the Microservice world: http://medium.com/swlh/handling-transactions-in-the-microservice-world-c77b275813e0 \ >[2] Event Driven Architecture: http://microservices.io/patterns/data/event-driven-architecture.html \ >[3] MonolithFirst: http://martinfowler.com/bliki/MonolithFirst.html >你好,我是俞凡,在Motorola做過研發,現在在Mavenir做技術工作,對通訊、網路、後端架構、雲原生、DevOps、CICD、區塊鏈、AI等技術始終保持著濃厚的興趣,平時喜歡閱讀、思考,相信持續學習、終身成長,歡迎一起交流學習。 \ >微信公眾號:DeepNoMind