在微服務(wù)架構(gòu)中的數(shù)據(jù)一致性
當(dāng)從傳統(tǒng)的單體應(yīng)用架構(gòu)轉(zhuǎn)移到微服務(wù)架構(gòu)時(shí),特別是涉及數(shù)據(jù)一致性時(shí),數(shù)據(jù)一致性是微服務(wù)架構(gòu)中最困難的部分。傳統(tǒng)的單體應(yīng)用中,一個(gè)共享的關(guān)系型數(shù)據(jù)庫(kù)負(fù)責(zé)處理數(shù)據(jù)一致性。在微服務(wù)架構(gòu)中,如果使用“每個(gè)服務(wù)一個(gè)數(shù)據(jù)庫(kù)”的模式,那么每個(gè)微服務(wù)都有自己的數(shù)據(jù)存儲(chǔ)。
因此,數(shù)據(jù)庫(kù)在應(yīng)用程序之間是分布式的。如果每個(gè)應(yīng)用程序使用不同的技術(shù)來(lái)管理它們的數(shù)據(jù),比如非關(guān)系型數(shù)據(jù)庫(kù),這種分布式架構(gòu)雖然在數(shù)據(jù)管理方面有許多好處,比如可伸縮性、高可用性、靈活性等,但在數(shù)據(jù)管理方面也存在一些關(guān)鍵問(wèn)題,比如事務(wù)管理、數(shù)據(jù)一致性/完整性等方面。
問(wèn)題:分布式系統(tǒng)中的數(shù)據(jù)一致性
對(duì)于單體應(yīng)用程序,通過(guò)ACID事務(wù),一個(gè)共享的關(guān)系型數(shù)據(jù)庫(kù)處理并保證數(shù)據(jù)的一致性。ACID 是一個(gè)縮寫(xiě),具體含義如下:
- A 原子性:事務(wù)的所有步驟要么全部成功,要么全部失敗,沒(méi)有部分狀態(tài),全有或全無(wú)。
- C 一致性:事務(wù)結(jié)束時(shí)數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)都是一致的。
- I 隔離性:同一時(shí)間只有一個(gè)事務(wù)可以訪問(wèn)數(shù)據(jù),其他事務(wù)必須等待當(dāng)前事務(wù)完成。
- D 持久性:數(shù)據(jù)在事務(wù)結(jié)束時(shí)被持久化到數(shù)據(jù)庫(kù)中。
為了保持強(qiáng)數(shù)據(jù)一致性,關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng)支持ACID特性。
但在微服務(wù)架構(gòu)中,每個(gè)微服務(wù)都有自己的數(shù)據(jù)存儲(chǔ),并采用不同的技術(shù)。因此,沒(méi)有中央數(shù)據(jù)庫(kù),也沒(méi)有單一的工作單元。業(yè)務(wù)邏輯被跨越到多個(gè)本地事務(wù)中。這意味著你不能在微服務(wù)架構(gòu)中的數(shù)據(jù)庫(kù)之間使用單一的事務(wù)工作單元。但你仍然需要在你的應(yīng)用程序中使用ACID特性。
讓我們用一個(gè)簡(jiǎn)單的樣例場(chǎng)景來(lái)解釋。在一個(gè)訂單管理系統(tǒng)中,可能存在庫(kù)存管理、支付和訂單管理等服務(wù)。假設(shè)這些服務(wù)都按照微服務(wù)架構(gòu)設(shè)計(jì),并應(yīng)用了“每個(gè)服務(wù)一個(gè)數(shù)據(jù)庫(kù)”的模式。為了完成訂單流程,訂單服務(wù)首先調(diào)用庫(kù)存管理服務(wù)進(jìn)行庫(kù)存控制和預(yù)留,訂單中的相關(guān)產(chǎn)品被預(yù)留,以防止賣給其他客戶。第二步是支付步驟。支付服務(wù)負(fù)責(zé)支付業(yè)務(wù)。訂單服務(wù)調(diào)用支付服務(wù),從客戶的信用卡中完成支付。由于每個(gè)服務(wù)都是獨(dú)立的,對(duì)分離的數(shù)據(jù)庫(kù)的更新在服務(wù)范圍內(nèi)被提交。最后一步是創(chuàng)建訂單記錄。在這一步中,假設(shè)發(fā)生了技術(shù)錯(cuò)誤,訂單記錄無(wú)法創(chuàng)建,訂單號(hào)無(wú)法發(fā)送給客戶,但已從客戶那里收到了付款。這里出現(xiàn)了數(shù)據(jù)一致性問(wèn)題。接下來(lái)我會(huì)在文章的“可能的解決方案”部分討論在這一點(diǎn)之后可以做些什么。
可能的解決方案
首先,沒(méi)有一種單一的解決方案適用于所有情況。根據(jù)具體情況,可以采用不同的解決方案。
解決問(wèn)題有兩種主要方法:
- 分布式事務(wù)
- 最終一致性
分布式事務(wù)
在分布式事務(wù)中,事務(wù)在兩個(gè)或多個(gè)資源上執(zhí)行(例如數(shù)據(jù)庫(kù)、消息隊(duì)列)。通過(guò)分布式事務(wù)管理器或協(xié)調(diào)器,跨多個(gè)數(shù)據(jù)庫(kù)保證數(shù)據(jù)的完整性。
分布式事務(wù)是一個(gè)非常復(fù)雜的過(guò)程,因?yàn)樯婕岸鄠€(gè)資源。
兩階段提交(2PC) 是一種阻塞協(xié)議,用于保證在分布式事務(wù)中所有事務(wù)要么全部成功,要么全部失敗。
XA標(biāo)準(zhǔn) 是2PC分布式事務(wù)的規(guī)范。JTA包括Xtandard API。符合JTA標(biāo)準(zhǔn)的應(yīng)用服務(wù)器支持Xtandard API。但所有資源必須部署到單個(gè)JTA平臺(tái)才能運(yùn)行2PC。對(duì)于微服務(wù)架構(gòu)來(lái)說(shuō),這不太合適。
分布式事務(wù)的優(yōu)點(diǎn):
- 強(qiáng)的數(shù)據(jù)一致性
- 支持ACID特性
分布式事務(wù)的缺點(diǎn):
- 維護(hù)起來(lái)非常復(fù)雜
- 由于是阻塞過(guò)程(不適合高負(fù)載場(chǎng)景),高延遲和低吞吐量
- 事務(wù)之間可能出現(xiàn)死鎖
- 事務(wù)協(xié)調(diào)器是一個(gè)單點(diǎn)故障
最終一致性
最終一致性是分布式系統(tǒng)中用于實(shí)現(xiàn)高可用性的模型。在一個(gè)最終一致性的系統(tǒng)中,允許一段時(shí)間的不一致,直到解決分布式數(shù)據(jù)的問(wèn)題。
這個(gè)模型不適用于跨多個(gè)微服務(wù)的分布式ACID事務(wù)。最終一致性使用BASE數(shù)據(jù)庫(kù)模型。
雖然ACID模型提供了一個(gè)一致的系統(tǒng),但BASE模型提供了高可用性。
BASE這個(gè)縮寫(xiě)代表:
- Basically Available:通過(guò)在數(shù)據(jù)庫(kù)集群的節(jié)點(diǎn)之間復(fù)制數(shù)據(jù)來(lái)確保數(shù)據(jù)的可用性
- Soft-state:由于缺乏強(qiáng)一致性,數(shù)據(jù)可能隨時(shí)間變化。一致性責(zé)任委托給開(kāi)發(fā)人員。
- Eventual consistency:BASE不可能立即提供一致性,但最終會(huì)提供一致性(在短時(shí)間內(nèi))。
SAGA 是一種操作最終一致性模型的常見(jiàn)模式:
(1) 基于協(xié)同的SAGA:在這種情況下,不存在中央?yún)f(xié)調(diào)器。每個(gè)服務(wù)在其任務(wù)完成后產(chǎn)生一個(gè)事件,并且每個(gè)服務(wù)監(jiān)聽(tīng)事件以采取行動(dòng)。這種模式需要一個(gè)成熟的事件驅(qū)動(dòng)架構(gòu)。
- 事件溯源:使用事件存儲(chǔ)來(lái)存儲(chǔ)事件變化狀態(tài)的方法。事件存儲(chǔ)是充當(dāng)事件數(shù)據(jù)庫(kù)的消息代理。通過(guò)重新播放來(lái)自事件存儲(chǔ)的事件來(lái)重建狀態(tài)。
- 基于協(xié)同的SAGA模式在事務(wù)中步驟較少時(shí)可以很好地工作(例如2到4個(gè)步驟)。當(dāng)事務(wù)中的步驟數(shù)量增加時(shí),很難跟蹤哪些服務(wù)監(jiān)聽(tīng)哪些事件。
(2) 基于編排的SAGA:協(xié)調(diào)器服務(wù)(Saga執(zhí)行編排器,SEG)負(fù)責(zé)根據(jù)業(yè)務(wù)邏輯對(duì)事務(wù)進(jìn)行排序。編排器決定應(yīng)執(zhí)行哪些操作。如果某個(gè)操作失敗,編排器會(huì)撤銷先前的步驟。這稱為補(bǔ)償操作。補(bǔ)償是在系統(tǒng)保持一致?tīng)顟B(tài)時(shí)發(fā)生故障時(shí)要執(zhí)行的操作。
- 當(dāng)數(shù)據(jù)已被不同的事務(wù)更改時(shí),撤銷更改可能已經(jīng)不可能。
- 補(bǔ)償必須是冪等的,因?yàn)樵谥卦嚈C(jī)制中可能會(huì)被調(diào)用多次。
- 必須小心設(shè)計(jì)補(bǔ)償。
有一些可用的框架可以實(shí)現(xiàn)Saga編排模式,例如Camunda、Apache Camel。
SAGA的優(yōu)點(diǎn):
- 在本地原子事務(wù)中執(zhí)行非阻塞操作
- 事務(wù)之間沒(méi)有死鎖
- 沒(méi)有單點(diǎn)故障
SAGA的缺點(diǎn):
- 最終的數(shù)據(jù)一致性
- 沒(méi)有讀隔離,需要額外的努力(例如,用戶可能會(huì)看到操作已完成,但在幾秒鐘后由于補(bǔ)償事務(wù)被取消)
- 當(dāng)參與服務(wù)數(shù)量增加時(shí),調(diào)試?yán)щy
- 開(kāi)發(fā)成本增加(需要實(shí)際服務(wù)開(kāi)發(fā)以及補(bǔ)償服務(wù)開(kāi)發(fā))
- 設(shè)計(jì)復(fù)雜
在維護(hù)分布式數(shù)據(jù)存儲(chǔ)之間的數(shù)據(jù)一致性可能非常困難。在設(shè)計(jì)新應(yīng)用程序時(shí)需要有不同的思維方式。我們可以說(shuō),數(shù)據(jù)一致性的責(zé)任從數(shù)據(jù)庫(kù)轉(zhuǎn)移到了應(yīng)用程序級(jí)別。
選擇哪種解決方案
解決方案取決于使用案例和一致性要求??偟膩?lái)說(shuō),應(yīng)考慮以下設(shè)計(jì)考慮因素。
(1) 盡可能避免在微服務(wù)之間使用分布式事務(wù)。使用分布式事務(wù)會(huì)帶來(lái)更復(fù)雜的問(wèn)題。
(2) 設(shè)計(jì)你的系統(tǒng),盡可能不要求分布式一致性。為了實(shí)現(xiàn)這一點(diǎn),識(shí)別事務(wù)邊界;
- 識(shí)別必須在同一工作單元中工作的操作。對(duì)于這種類型的操作使用強(qiáng)一致性
- 識(shí)別可以容忍一致性方面的可能延遲的操作。對(duì)于這種類型的操作使用最終一致性
(3) 考慮使用事件驅(qū)動(dòng)架構(gòu)進(jìn)行異步非阻塞服務(wù)調(diào)用
(4) 通過(guò)補(bǔ)償和協(xié)調(diào)過(guò)程設(shè)計(jì)容錯(cuò)系統(tǒng),以保持系統(tǒng)的一致性
(5) 最終一致性模式需要在設(shè)計(jì)和開(kāi)發(fā)方面進(jìn)行思維方式的轉(zhuǎn)變
結(jié)論
微服務(wù)架構(gòu)具有諸如高可用性、可伸縮性、自動(dòng)化、自治團(tuán)隊(duì)等很多優(yōu)點(diǎn)。為了最大程度地發(fā)揮微服務(wù)架構(gòu)風(fēng)格的效率,傳統(tǒng)方法需要進(jìn)行一些改變。數(shù)據(jù)和一致性管理是需要仔細(xì)設(shè)計(jì)的。