微服務分布式事務的一些思考

2022-07-03 20:12:11 字數 3262 閱讀 8666

關於微服務分布式事務的一些思考,筆者沒有參與過複雜分布式事務的場景,各位大神路過可以分享一些遇到的案例,大家一起**。

關於分布式事務,筆者推薦的處理方法是「盡量避免」,如果實在避免不了(這已經是高併發、使用者量比較多的**了)則使用「最終一致性」處理(參照cap理論base思想),如果處理了事務,但還是遇到了資料錯誤,那還有最後一道保障,那就是「日誌」,可以通過日誌找回資料,其實大部分網際網路公司也都是這麼做的。說到「盡量避免」,可能有人認為這不是乙個解決問題的辦法。這裡舉個例子,我們生活在城市,其實是每天面臨危險的,走路有可能遇到天上掉下的板磚,坐車有可能遇到酒駕,甚至不少人摔個跟頭都能淹死,這些低概率的危險我們無法避免。但如果你生活在乙個交通事故頻發的地段,如果你還有小孩,這個時候你最先想到的解決辦法是什麼?是提議**改善公路構造?還是立刻搬走?相信,正常人都會選擇搬走,這就是我們所說的「盡量避免」,在微服務應用中如果無休止的建立跨事務服務,那麼事務的複雜性也將永無止境,如下圖:

這棵服務呼叫樹如果繼續延伸下去,相信到了某一時刻,神仙也解決不了出現的問題。其實,即使是資料庫本身它們的事務也不是能夠百分百保證,就拿oracle來說,遇到突然停電或宕機照樣丟失資料。筆者有一次安裝了sap的乙個系統,資料庫用的是oracle,結果一次停電後竟然連表都整個丟失。sap的系統足夠強大了,oracle也足夠強大,當兩個複雜的系統相遇,出現的問題將更複雜。每次遇到資料庫丟資料,最終解決辦法都是看日誌找回資料。從這裡也可以看出「日誌」是事務或者說資料一致性的最後保障。

在複雜的服務呼叫結構中我們遇到的最困難的問題是「併發性」問題。如上圖,a、b兩個介面同時呼叫了c介面,如果a、b是同時呼叫的,如何保證c呼叫的有序性?假如c就是電子商務中的購買介面,我們知道某商品購買是需要有序執行的,因為每購買一次商品數量會減少。這個時候顯然需要為a、b設定乙個鎖機制,保證a、b介面的呼叫有序性,比如我們在共享記憶體中建立a、b的鎖:      

鎖名稱a-b   鎖值0

那麼不管什麼時候要執行a、b介面都需要來查詢鎖的值,如果鎖的值是0,就修改為1,並執行,執行完後將該鎖值修改為0,表示釋放該鎖。這只是執行成功的情況,如果a、b其中乙個執行失敗,情況就更複雜了。假設a、b都執行c成功,但a在執行d時失敗,那麼a需要回滾c的資料,但此時b正在提交c的資料。此時如何處理?此時我們採用「最終一致性」方法,處理的方法就是,你把執行上下文和錯誤情況作為事件進行「記錄和匯報」。記錄可以是記錄日誌,匯報可以是傳送訊息給訊息佇列,或者傳送給乙個專門處理分布式事務錯誤的處理單元。這很像現實中我們處理問題的方法,比如家裡進小偷了,警察來後會這樣問:在什麼情況下(執行的上下文),你丟了什麼(產生了什麼呼叫失敗),只有把這些匯報給他後他才能處理。

如果是上面這個例子,c代表購買介面,那麼你需要傳送的資料報括:a在執行c時,c所操作的資料的最初值(上下文資料),以及c最後把資料更新到了什麼值(錯誤資訊)。這樣分布式事務錯誤處理單元即使在b提交了事務後也可以對a所操作的c進行回滾,這個回滾方法也要經過程式設計的,並不是直接恢復某個值那麼簡單,很可能有複雜的處理邏輯。

現在看到了,只是呼叫樹最外層a、b兩個服務的呼叫就如此複雜,那麼如果有幾百個服務,服務之間又有錯綜複雜的呼叫,那情況都不可想象。此時有些情況估計xa分布式事務也無法處理,因為它並不知道執行的上下文,這個上下文是你自己編寫,更何況還有一些在xa之外的外部介面,還可能有除資料庫之外的其它儲存方式包含在事務當中。不過還是可以通過上面我們所說的方法進行處理的,因為再複雜也不可能超過現實中事物的複雜。當然,最好的辦法還是我們之前提到的「盡量避免」,那屬於防患於未然。要盡量避免可以從以下方面去做(可能不全面):

1)  避免出現迴圈呼叫。

2)  盡量避免出現併發性呼叫。

3)  服務要分層,上層介面只能呼叫下層介面。

4)  事務強相關性表盡量放同乙個庫。

5)  可以將事務性操作,封裝成乙個介面,在**級處理跨庫事務。

其中1)是要完全避免的,這根我們執行緒程式設計一樣的,如果出現a呼叫b,b又呼叫a,執行緒是很容易死鎖的,對於介面不會出現死鎖但會出現資料丟失,而且是毫無蛛絲馬跡的丟失。至於5),在**級處理跨庫事務有很多方法,xa就是其中一種,當然因為集群問題(只支援單點部署,無法部署高可用集群),也要慎用。

微服務中分布式事務的使用涉及「利益」和|「風險」的關係。利益和風險永遠成正比。這裡的利益是指:系統的執行效能提公升、開發成本減少等,風險是指:資料不一致、效能下降等。分布式事務是減少風險,但又同時減少了利益。所以,這是個要權衡利弊的操作,沒有也不會存在乙個完美的方案,只會存在乙個適用於某種情況下的方案。孢子框架在這裡推薦兩種輕量級方案:

1)  假如有介面c,實現c的回滾介面,a呼叫c、d,如果c呼叫成功,d呼叫失敗,a中直接呼叫c回滾介面。

2)  假如有介面c,實現c的回滾介面,a呼叫c、d,如果c呼叫成功,d呼叫失敗,a中傳送c呼叫上下文及錯誤資訊到訊息佇列,訊息佇列持久化該訊息,c監聽該訊息並呼叫回滾介面,或者專門的事務失敗處理服務監聽該訊息並呼叫c的回滾介面。 

a、  c服務因高負載宕機

b、  c服務所關聯資料庫宕機

c、  c服務因網路原因呼叫不了

d、  c服務本執行緒死掉

其中a比較嚴重,此時會積累大量的c回滾請求沒有處理,所以這種情況下宕機,資料失敗的比較多。至於c概率較小,因為前面你已經呼叫c成功了,說明網路沒問題。至於b就概率更小了,而且資料庫宕機這種情況一旦發生恐怕後果不僅僅是分布式事務的問題了,這個問題要在資料庫方面解決。剩下的d,說明編碼邏輯有問題,這就要求提高編碼的水平,保證只要不是外設和停電的問題,都能執行正常,需要程式設計方面的技巧。那麼可以看來,只有a才是限制這種方法的最根本原因,所以在併發不是很高的情況下,該方案基本不會出現什麼問題。

方案2)其實也是很多專家們提到的了。就是先將回滾資訊持久化,然後再處理回滾。這種情況也會失敗,具體失敗原因應該和xa這種分布式方案差不多了,分析如下:

a、  停電

b、  訊息佇列不可用(xa的tm不可用)

至於b只要使用高可用的訊息佇列集群就可以避免,或者監控訊息佇列的執**況,避免因儲存空間不足而宕機。這個方案失敗基本是因為停電等一霎那程序失敗所導致。比如正在向訊息佇列傳送回滾事件的一霎那停電,比如正在回滾資料時資料庫一霎那停電等等。這種程序內失敗的問題,任何系統都無法避免,但這畢竟是小概率事件。所以這個方案有著比xa更高可靠性(xa是不能部署集群的)。其實,包括oracle等資料庫以及office裡面的word等文書處理工具,都是用類似方案處理。比如orcale,他會記錄事務裡面的每一步操作,如果事務失敗它就把這些操作進行反向操作,如果你刪除了一條資料,它此時就增加這條資料,如果你修改了某欄位,它就把某欄位恢復舊值(要記錄舊值,這很關鍵)。所以,這個方案看上去很簡單,但回滾處理比較複雜,回滾處理要按業務場景處理,也就是按失敗上下文去處理。

分布式的一些思考

最有效率的分布式是在執行方法前知道所執行的方法使用的資料即所謂環境,並把相關資料和方法本法放到指定的機器上執行,返回結果給指定的客戶端。在方法本身不確定的前提下,所有資料都是環境一部分。如果使用統一資料伺服器的方法,網路和硬碟的開銷抵消了分布式的優勢。因為大部分操作無外乎就是把資料簡單操作後放到新的...

MQ 分布式事務 微服務應用

以支付 電商下單為例子。乙個電商系統包含了好幾大類模組,就比如有使用者模組 商品模組 庫存模組 訂單模組 支付模組 物流模組,活動模組等,以下就先列舉幾個最基礎常見的模組 使用者模組 商品模組 庫存模組 訂單模組 支付模組 使用者流程如下 如果系統規模較小,資料表都在乙個資料庫例項上,專案服務端也都...

事務例子 分布式事務一些心得

答 事務。舉個例子 使用者下了乙個訂單,需要修改餘額表,訂單表,流水表,於是會有類似的偽 start transaction curd table t account any exception rollback curd table t order any exception rollback c...