標籤:

微服務如何處理分散式事務?

看到過TCC的解決方案,但還是偏理論的,具體如何落地呢?


謝邀~

不過,我沒有做過分散式事務……(汗)~~

以前負責社交網路,貌似除了支付,其他都不需要事務……(消息,組操作,好友,UGC,計數等,都用不到……支付系統一直沒出過事,我也沒去看過-_-b……)一般的遊戲也用不到(很多都是可以合理解釋的,即使是A玩家掠奪B玩家資源的同時,B玩家在消耗資源建造道具抵抗,也能從遊戲邏輯到客服做出合理解釋,避免需要鎖定很多系統資源的事務操作)。

簡單查了一下,認為兩階段提交靠譜。

另外,如果能完全掌控後端的設計架構的話,即使是之前聽別人提到過的電商的超售,和秒殺,應該也是可以通過將流程分解然後關鍵部分hash到唯一確定的數據單節點進行事務操作的。(當時所在公司就處於這麼一個狀況,我們設計的後端都沒有事務,直接分解了繞過去-_-b……)

還是等做過的大牛出來解答吧~~

PS:最近太忙,現在才來回答,不好意思~~

-=--=--=--=--=--=--=--=--=--=--=--=-[2015-09-02 更新分割線]-=--=--=--=--=--=--=--=--=--=--=-

根據題主的追加評論更新:

如果我來做的話,我可能會按照下面的方案實施。

可能比較羅嗦,見諒~~:)

在開始之前,先看兩個問題:

1. 許可權假設和業務拆分:

前面回答時,我曾說,如果能擁有系統後端的整體架構權,可以通過將流程分解然後關鍵部分hash到唯一確定的數據單節點進行事務操作。其實如果只擁有事務相關的服務架構權,和事務相關數據的控制權,也可以進行同樣的分解。

根據題主問題的描述,覺得題主應該擁有以上兩項職權。

基於以上許可權假設,我們可以將分散式事務設計為一個子系統(一級子系統)。該子系統可以是虛擬系統,因為我們可能有分散式數據的控制權,但不是唯一控制權,或者所有權。畢竟提供事務相關數據的服務不一定是我們能修改的服務。這一點會對後續的設計產生一定的影響,但主體思想不受該情況影響。

然後我們可以將需要處理的事務分類,盡量地拆開,分布在不同的服務實例上。但如何拆分和具體情況關係非常密切,所以我這隻能據一個簡單的例子:數字化之前的期貨交易。大豆期貨的交易池和小麥期貨的交易池絕不是同一個交易池。不然一群交易員一個報大豆的合約,一個報小麥的合約,交易池和市場很快就亂套了。根據這個例子,對應到我們的系統上,事務需求提交者將相關的事物信息提交給虛擬系統的虛擬網關(這個虛擬網關不一定需要是真實存在的獨立服務),在這裡簡稱為gateway。gateway根據事務信息,將事務分類,然後發送給不同的事物子子系統(二級子系統)處理。同一類事物如果相互之間資源比較獨立,比如遊戲中玩家1v1對戰,那如果事務壓力比較大,就可以將該類事務進一步通過hash拆分到不同的獨立服務上處理。

如果這樣拆分後,最後的事務仲裁者是資料庫,相關數據又在同一個庫里,那麼這時直接交給資料庫事務進行處理就行。後面的回答直接跳過不必再看。

2. 分散式事務危險期:

能看到這裡的,說明經過前一步的拆分,最後的事務仲裁者是分散式的服務,而不是唯一的數據節點或者資料庫伺服器。那麼,剩下的選擇就是兩階段提交。

兩階段提交一般會選擇一個事務協調者,或者事務控制者,然後向不同的數據持有服務提交事務申請(第一階段,預提交),全部確認後,會再進行事務確認(第二階段)。危險期就在於,兩個階段中,事務協調者宕機怎麼辦?傳統的方法是通過寫入日誌,然後事後恢復。但這裡有幾個問題:

a. 事務恢復的時效?預提交階段凍結的數據怎麼處理?如果預提交凍結的數據有超時自動釋放機制,那在恢復事務時,將會帶來更多的不一致和分支情況。

b. 如果不是資料庫,那寫日誌文件好辦,恢復呢?增加多少工作量?

c. 如果事務協調者宕機,且磁碟損壞,怎麼按日誌回復?如果是分散式日誌系統,那恢復的複雜度和周期,以及恢復的協調過程將更加複雜。

所以為了簡單易維護,易開發起見,為了秉承:「簡單、粗暴、高效、穩定」的指導方針,我可能採取以下的兩階段提交實施方案:

1. 協調者不是唯一一個,而是選取三個輪庄。

2. 日誌該記就記,可以用來恢復事務,但這不是重點。只是最後的手段。

3. 一個事務內部,根據不同的資源所在,細分每個資源的子操作任務號。每個數據服務提供者,需要有能力記錄當前未完成事務的子任務號和對應的結果。以及數據服務提供者自身宕機後的數據狀態恢復。

要求記錄當前未完成事務的子任務號和對應的結果,是為了允許當協調者宕機,相關狀態沒有同步到後續協調者上時,後續協調者重放任務,能獲取到原始任務的結果,而不至於重複執行。這樣要求數據服務提供者,如果遇到重複的子任務號,直接返回該子任務最初的執行結果而不需再次執行。

4. 各階段可以使用事務超時機制,目的是為了儘快解凍失效事務凍結的資源,盡量避免對其他業務的影響。

預期效果:

1. 即使在協調者宕機的情況下,只要還有一個協調者存在,就能完成事務,大大減小需要恢復事務的概率。

當這概率減小到百分之一,千分之一的時候,很多的保全手段,比如日誌恢復,應急處理等,基本上就不沒有繼續存在的必要。畢竟多一台協調者服務的成本,比使用這些保全機制的開發成本、維護成本、事務的恢復成本要小得多得多。

當然,萬一最不幸的情況發生了怎麼辦?這就好比一個服務擁有以上所有保全措施,但還是遇到了所有機器因地震或者災害不可恢復的情況。那就是用相同的最後預案。該手工對賬的就的手工對賬。逃不掉的。

如果多協調者的存在,能使總成本(開發、維護、恢復、人工、部署等等)大為降低,且使事務需要恢復的概率降到了地震或其他災害同一概率的級別時,那其他的保全手段的存在,也就只剩一個心理安慰的價值。

2. 如果所有的協調者全部宕機(這簡直就是500萬頭等獎的概率,快去買彩票吧),如果有事務超時機制存在,就能盡量的減少不可用的凍結資源,盡量不影響其他無關的業務。但要是影響了怎麼辦?和上面說的一樣,地震來了,該怎麼辦就怎麼辦,類似。

如果真要把數據一直凍結下去,直到事務恢復,也不是不行,看具體業務具體需求。

如果第一個預期效果真的把失敗概率降到足夠低,那其實事務超時機制也是多餘。

而且如果有超時機制在,可以自動將事務異常中斷自動彙報到報警系統,然相關人員能很有針對性地處理。當然,如果全部相關機器都宕機了,沒有人報警,那就和地震來了一樣,該咋辦還咋辦。

好了,基於以上的前提,設計和預期,具體的實施方案是這樣的:

1. gateway選取第一協調者(選自己也沒事,隨意),然後第一協調者選取第二協調者和第三協調者(最少不在一台機器上,最好都不在一個機架上)。第一協調者告訴第二協調者其身份,事務,第三協調者地址;同時第一協調者也告訴第三協調者其身份、事務、第二協調者地址。

2. 第一協調者向相關數據服務提交資源凍結請求(第一階段),並將凍結請求的相關情況(目標機器、凍結資源對應在事務中的任務號等),同步到第二、第三協調者上去。

這裡的重點是,所有請求和同步都是非同步並發操作,避免引入串列操作導致的大量延遲的問題。

3. 如果2成功,第一協調者向相關數據服務提交事務提交請求(第二階段),並同步到第二第三協調者上去。

這裡也是要求非同步並發同步。

4. 如果2失敗,第一協調者向相關數據服務提交回滾操作,並同步到第二第三協調者上去。然後向事務申請者返回事務失敗的應答。

5. 如果3失敗,那說明是相關的數據服務宕機或者錯誤。這個在將情況同步到第二、第三協調者上後,按相關服務正常的預案處理就行。比如重新找一台機器恢複數據狀態,然後完成該節點上的事務資源使用。(數據資源服務節點的狀態恢復屬於數據服務自身的設計,和分散式事務恢復沒有直接關聯 。如果所請求的數據服務/服務集群不能提供數據狀態的恢復功能,可能就要考慮去鞭策相關服務的開發人員滿足需求,甚至更換數據服務)

6. 如果3成功,將成功情況同步到第二第三協調者上去。然後向事務申請者返回事務成功的應答。

7. 在完成事務後,告訴第二、第三協調者整個事務操作完成,其身份解除。整個分散式事務到此完成。

異常情況:

1. 如果第一協調者宕機,那第二協調者將會通過網路IO事件、超時、或者輪訓等機制比較及時的得知第一協調者無法工作,那第二協調者依據自身保存的事務進度,繼續協調事務。因為數據服務節點被要求記錄事務資源凍結等子任務的任務號和對應的結果,且允許同一子任務重放,所以不用擔心雙重凍結或者雙重提交。即使數次發送重複請求也沒有關係。

2. 如果第一、第二協調者宕機,第三協調者能通過類似的方式,得知前兩者不能繼續協調,從爾接管整個事務的協調。

3. 如果三個協調都宕機,是否考慮每宕機一個協調器,就自動增加一個備選協調器?

4. 如果同時都宕機,……,最近的一個彩票售賣點在哪裡?

以上。

根據 steve zhang 的提醒,以上的方案僅限於同一內網。如果是跨機房或者類似的分散式事務,還需要處理網路分裂的情況。這個處理起來就更加複雜。如果有需要,這個另行討論。


TCC是分散式事務實現的一種方式


TRYING 階段主要是對業務系統做檢測及資源預留


CONFIRMING
階段主要是對業務系統做確認提交,TRYING階段執行成功並開始執行CONFIRMING階段時,默認CONFIRMING階段是不會出錯的。即:只要TRYING成功,CONFIRMING一定成功。


CANCELING 階段主要是在業務執行錯誤,需要回滾的狀態下執行的業務取消,預留資源釋放。

舉個支付項目的例子:


支付系統接收到會員的支付請求後,需要扣減會員賬戶餘額、增加會員積分(暫時假設需要同步實現)增加商戶賬戶餘額


再假設:會員系統、商戶系統、積分系統是獨立的三個子系統,無法通過傳統的事務方式進行處理。


TRYING階段:我們需要做的就是會員資金賬戶的資金預留,即:凍結會員賬戶的金額(訂單金額)

CONFIRMING階段:我們需要做的就是會員積分賬戶增加積分餘額,商戶賬戶增加賬戶餘額


CANCELING階段:該階段需要執行的就是解凍釋放我們扣減的會員餘額


以上所有的操作需要滿足冪等性,而冪等性則是指業務方法調用一次與調用多次的執行返回結果是一樣的。冪等性的實現方式可以是:


1、通過唯一鍵值做處理,即每次調用的時候傳入唯一鍵值,通過唯一鍵值判斷業務是否被操作,如果已被操作,則不再重複操作


2、通過狀態機處理,給業務數據設置狀態,通過業務狀態判斷是否需要重複執行


另外:你也可以看下這篇博客,上面是以傳統電商平台支付系統為例的一套分散式事務處理實現,講的也比較深。http://www.roncoo.com/article/detail/124243


我來

一種是中間件解決,思路就是兩階段提交,保持最終一致性。

還有一種是在業務上,做到重試和冪等。

client,做某個動作,一次不成功,retry。重試

server,對於一個業務操作,調用多次,結果相同。冪等


凡是遇到事務問題,利用資料庫來做是最偷懶、最安全、最穩妥、最容易推卸責任的上策。


贊同「盡量避免分散式事務」的忠告。

但分散式事務也確實不是說避免就能避免的,贊同「最終一致性」的解決方案。

針對微服務架構下的數據一致性實踐,可以參考這個回答,幾種工程中用得較多的解決方案都有解析。

常用的分散式事務解決方案介紹有多少種?——網易雲的回答

關於題主特別關注的TCC,這裡再補充:

  • 補償模式的特點是實現簡單,但是想形成一定程度的通用方案比較困難,特別是服務
    鏈的記錄,因為大部分時候,業務參數或者業務邏輯千差萬別。
  • 另外,很多業務特徵使得該
    服務無法提供一個安全的回滾操作。


答案都好複雜。

領域驅動設計早已闡明,具有強一致性要求的一組業務概念,屬於同一個聚合,不建議拆到不同服務中,從而儘可能避免分散式強事務一致性的處理。

而可以拆分的服務邊界,是在限界上下文或者聚合的粒度上。這樣的事務一致性屬於最終一致性,可以用成熟的工具或者演算法處理。


分散式事務是一個單獨的領域,解決起來一直很棘手,目前來看沒有好的解決方案,不管微服務出不出現。

實際工作中,除了支付方面,碰到事務的場景真的很少。如果真有那麼極個別的地方碰到事務,別拆分就好了,直接一個服務內部處理好了。乾淨利落,比分散式事務更簡潔、穩定


分散式事務提交本質上一個consensus問題,可以參考一下當前最流行的consensus演算法:

Paxos (computer science)

Raft (computer science)


最簡單的做法是交給支持分散式事務的資料庫,比如 TiDB。如果想自己折騰典型的做法是:引入分散式隊列加上人肉手工的二階段提交。


可以考慮用微服務共享的分散式資料庫直接搞定嗎?


根據他人博文畫了一個簡單的圖,可以將就看看


大概看了一些答案, 都是比較提傳統的方法. 比如一致性演算法和兩步提交. 這些辦法其實是不推薦的.

真正的微服務架構. 由於領域分解比較困難加上嚴格的服務(數據)隔離要求, 使用聚合根和 CQRS 的事件流驅動型構架才是更推薦的選擇. 這樣的構架也更容易和大數據處理的 Lambda 構架相融合.


所有的分散式事務都離不開2PC的思想,但實際業務場景中一般不需要實現2PC所有的步驟。

大多數場景的業務只需要實現一個正向操作,一個逆向操作,就可以通過不斷重試達到最終的一致性,這個正向操作可以認為是2PC中的prepare階段,逆向操作可以認為是rollback階段。

還有一類業務,只需要實現一個正向操作就可以了,比如用戶購買一個物品成功後增加積分或經驗值的操作,這種操作可以通過不斷的重試最終成功,這個操作可以認為是2PC中的commit階段。

當然也有需要實現2PC所有階段的業務,但這類的業務少之又少。

針對以上場景,可以設計一個分散式事務協調器,實現2PC的機制,並且可以根據業務的實際情況運行。TCC就是這樣的一個事務協調器,但過於嚴格了,要求業務實現的介面太多。


對於微服務架構,盡量避免分散式事務,這是Martin老爺爺說過的。

贊成以上David Wang的答案。

實在不行採用最終一致的解決方案。


強一制性場景除了2PC沒有其他更好的選擇,這裡主要討論業務上能夠接受「最終一致性」的場景。

目前我們使用DTS協調服務實現「最終一致性」場景和發生失敗時的「補償」場景,簡單說來由以下幾個步驟,使用plantuml表示,可以打開Open-source tool that uses simple textual descriptions to draw UML diagrams.把下面的字元串貼進去查看流程, 目前的實現還是基於資料庫、MQ和Redis實現的,主要是事務執行計劃、補償計劃及各種狀態檢查和重試,plantuml請參考:

@startuml
title 微服務應用場景調用(使用協調服務)
actor 應用系統
database DBMS
autonumber
應用系統 -&> DBMS : 啟動事務
應用系統 &<-- DBMS : ACK 應用系統 -&> DTS : 啟動事務(TransactionManager#beginTransaction)
應用系統 &<-- DTS : ACK 應用系統 -&> DBMS : 本地資料庫操作SQL
應用系統 &<-- DBMS : ACK 應用系統 -&> 應用系統 : 聲明服務A調用(TransactionManager#execute)
應用系統 -&> 應用系統 : 聲明服務B調用(TransactionManager#execute)
應用系統 -&> DBMS : 寫本地事務log(TransactionManager#confirm)
應用系統 &<-- DBMS : ACK 應用系統 -&> DBMS : 提交事務
應用系統 &<-- DBMS : ACK 應用系統 -[#FF0000]&> DTS : 提交事務調用聲明(TransactionManager#commit)
應用系統 &<-- DTS : ACK 應用系統 -[#00FF00]&> 應用系統 : 同步調用結束, 後續為非同步流程 - 即DTS協調
DTS -&>o 應用系統 : 協調應用系統執行申明的服務A調用, 定期重試直到成功或回滾
應用系統 -&> 服務A : 同步調用服務A(冪等操作)
應用系統 &<-- 服務A : ACK DTS &<-- 應用系統 : ACK DTS -&>o 應用系統 : 協調應用系統執行申明的服務B調用, 定期重試直到成功或回滾
應用系統 -&> 服務B : 同步調用服務B(冪等操作)
應用系統 &<-- 服務B : ACK DTS &<-- 應用系統 : ACK DTS -&>o 應用系統 : 通知事務執行結果, 定期重試直到成功
DTS &<-- 應用系統 : ACK @enduml


簡單的: 定期校對,針對每一個步驟,創建一個task並設置超時時間,此task定時檢測此步驟的成功與否。步驟正確完成則關閉此task,否則超時後回滾。


推薦閱讀:

TiKV為什麼用一個單點的授時服務而不是用一致性集群來授時呢?
三階段提交協議如何避免協調者狀態未知的情況?
常用的分散式事務解決方案介紹有多少種?
如何理解阿里大神程立的分散式事務文檔?
阿里雲的分散式事務中間件是什麼實現的?

TAG:分散式事務 |