本文翻譯至Dilfuruz Kizilpinar撰寫的Data Consistency in Microservices Architecture。
前言
本篇是作者Dilfuruz Kizilpinar在Garanti BBVA(一間土耳其的銀行)從傳統monolithic架構遷移到microservice架構時對於資料一致性的經驗分享。
最近工作開發的系統是一個基於database per service pattern設計的系統,每個服務都有各自的資料庫而非存取同一個資料庫的分散式的資料庫(distributed databases)設計,雖然如此帶來高可用性(high availability)與可擴展性(scalability)等好處,但面臨的最大挑戰是缺少單一資料庫的交易/事務管理(transaction management)特性所達到資料一致性(data consistency)。這也是CAP定理陳述的現象。在搜尋相關文章時查到此篇覺得解釋淺顯易懂,所以特別記錄下來。
資料一致性是微服務架構中最困難的部分。在傳統的單體式應用(monolithic application)是由一個共用的資料庫來確保資料一致性。然而微服務架構的database per service pattern每個服務各自擁有專屬的資料存儲(data store),資料庫是分散(distributed)到每個服務應用間。每個應用程式又可能使用不同的存儲技術,例如有的可能使用關聯式資料庫,有的使用NoSQL資料庫。雖然分散式架構有擴展性、高可用性,敏捷性等好處但在交易/事務管理及資料一致性/完整性方面有些需要考慮的重點。
圖1. 從單體式架構遷移到為服務架構圖
分散式架構中每個微服務各自擁有運行環境及資料存儲,所以資料是高可用且容易擴展的。
問題:分散式系統的資料一致性
單體式應用程式利用同一關聯資料庫的ACID特性來確保資料一致性。
ACID為以下意思的簡寫。
- Atomicity 原子性 - 一個交易中的動作要全部成功,否則全部失敗。
- Consistency 一致性 - 交易結束後資料維持一致(指完整性)。
- Isolation 隔離性 - 同時只允許一筆交易接觸資料,其他交易必須等待。
- Durability 持久性 - 交易結束後資料永久地保存在資料庫。
關連式資料庫支援ACID特性以確保資料的強一致。
圖2. 單體應用程式交易循序圖
由於微服務架構中的每個微服務各自擁有技術不同的資料存儲,所以沒有一個集中的資料庫,也就沒有單一交易/事務(single unit of work),商業邏輯橫跨分散在多筆交易。這代表無法在微服務中的多個資料庫中實現單一交易作業,但應用程式仍需要ACID特性來保證邏輯的正確。
圖3. 微服務互動圖
下面使用一個範例情境來解釋為何需要ACID。在一個訂單管理系統,可能包含如庫存管理、支付及訂單管理等服務,假設這些服務以微服務架構的database per service pattern來設計。
要完成一筆下訂單程序,訂單服務首先要呼叫庫存管理服務來控制庫存及保留產品避免被其他再次修售給其他顧客。第二步為支付步驟。支付服務負責支付業務,訂單服務呼叫支付服務來完成顧客信用卡的付款。既然每個服務各自獨立,則各服務在資料庫的更新提交範圍限於各自的服務範圍內。最後一步為建立訂單資料。假如這步驟發生技術錯誤導致訂單未成功建立,則訂單號碼無法回送給顧客但卻已收到顧客的付款,此處即發生資料不一致的問題。在後面可能的解決方案會談到可處理的方式。
圖3. 微服務交易循序圖
可能的解決方案
首先要說並沒有單一的解決方案來應付不同案例,需依使用案例來決定可採取的方案。
解決方案主要有兩種:
- Distributed transactions 分散式交易管理
- Eventual consistency 最終一致性
Distributed Transactions 分散式交易管理
在分散式交易管理中,交易執行於兩個或以上的資源(e.g. 資料庫,訊息佇列)。藉由分散式交易管員(distributed transaction manager)或協調員(coodinator)來保證跨資料庫的資料一完整性。
分散式交易管理牽涉多個資源因此是一個非常複雜的程序。
2PC(二階段提交)是用來保證分散式交易中所有的交易同時成功或失敗的協議。
XA標準(XA)是2PC分散式交易的規範,JTA包含了XA的標準API。JTA合規的應用伺服器支援XA開箱即用,但所有的資源必須部署在單一的JTA平台以實現2PC,這方法並不適用於微服務架構(因為每個服務的資料庫是各自獨立,無法部署在單一的Server。)
分散式交易管理優點
- 資料強一致性。
- 支援ACID。
分散式交易管理缺點
- 過於複雜而難以維護。
- 阻塞程序導致的高延遲低吞吐(效能差),不適用高負載情境。
- 可能發生交易間的死鎖(dead lock)。
- 交易協調員會是單點故障(single point of failure)。
Eventual Consistency 最終一致
最終一致性是分散式系統達成高可用性的一種模型。最終一致系統中,在解決分散式資料問題前的短暫的資料不一致是被允許的。
最終一致性模型不使用跨服務的ACID特性,而是使用BASE資料模型。ACID模型提供資料一致性的系統,而BASE模型提供高可用性系統。
BASE是下面意思的縮寫:
- Basically Available - 藉由複製資料於資料庫叢集的多個節點來確保可用性。
- Soft-state - 由於資料強一致的鎖定可能隨時間改變,資料一致的責任轉移到開發人員身上。
- Eventual consitency - 立即的資料一致無法滿足,但最終資料在短時間後仍會一致。
SAGA pattern是常用來操作最終一致的模式。
SAGA pattern是一個基於一連串的服務操作的非同步(asynchronous)模型。在Saga pattern中分散式交易藉由非同步各微服務內的交易來實現。每個服務在所屬的交易中更新擁有的資料,由Saga管理服務的執行順序。
兩個最常見的Saga交易管理的實現為:
- Choreography-based SAGA 協同式SAGA
- Orchestration-based SAGA 編排式SAGA
Choreography-based SAGA
沒有集中的協調者,每個服務在完成任務後產生事件,其他服務監聽事件並採取後續行動。這個模式需要成熟的事件驅動架構。
- 事件來源(event source)是一個儲存事件異動狀態的存儲,事件存儲(event store)是一個做為訊息資料庫的訊息代理(message broker)。事件狀態可透過重放存儲中的事件來重建。
- Choreography-based SAGA pattern在交易步驟不多時(e.g. 2到4個步驟)可運作良好。不過當交易中的步驟變多時會變得難以追蹤服務間的事件監聽狀況(複雜難以維護)。
Orchestration-based SAGA
由一個編排員(Saga Execution Orchestrator, SEG)負責依商業邏輯來安排交易順序。編排員(orchestrator)決定操作順序,若操作失敗編排員會反做(undo)之前的步驟,又稱為補償操作(compensation operation)。補償是指當失敗發生時保持系統狀態一致而進行的動作。
- 當資料被其他交易變時,undo的回復變得不可行。
- 補償動作必須是冪等的(idempotent),因為在重試時可能會被呼叫多次。
- 補償動作的設計必須謹慎。
已經有一些實作Saga orchestration pattern的框架如Camunda、Apache Camel。
SAGA優點
- 基於服務本地原子交易的非阻塞操作。
- 交易間無死鎖。
- 無單點故障。
SAGA缺點
分散式資料存儲的一致性維護非常困難,需要用不同的思維來設計新的應用程式。
分散式系統的資料一致性的責任從資料庫移轉到應用層。
選擇哪個方案
解決方案決定於使用案例及一致性需求。
通常需考慮以下幾點:
- 避免使用跨微服務的分散式交易管理,因為這反而帶來更複雜的問題。
- 設計不需分散式一致性的系統,透過辨識以下交易邊界(transaction boundaries)來達成。
- 分辨出需在同一交易完成的操作,對這些操作使用強一致性。
- 分辨出可容忍延遲一致性的操作,對這些操作使用最終一致姓。
- 考慮使用事件驅動架構(event-driven architecture)來做非同步無阻塞的服務呼叫。
- 設計可容忍錯誤的系統,並由補正或校正程序來維持一致。
- 最終一致性模式需要以不同的思維來設計及開發。
結論
微服務架構具有許多優點如高可用性、擴展性、自動化、自治團隊(autonomous teams)等。要從微服務架構獲得最大的效益需要改變許多傳統方法。資料的一致性管理是一項必須小心設計的議題。
沒有留言:
張貼留言