1. 程式人生 > >分散式事務的總結與思考

分散式事務的總結與思考

投稿作者:文刀(個人微信公眾號:jishuhui_2015),Java Web全棧工程師,高階架構師,技術佈道者。曾任兩家上市公司的技術主管,從事微服務架構設計,DevOps團隊建設工作,在電商、LBS、IoT等相關應用領域有豐富的專案經驗。

思來想去,個人覺得要理解「分散式事務」,必須先知道什麼是“事務(Transaction)”。

當然,這裡提到的“事務”是在事務型資料庫(Transactional Database)知識領域內的。

一個事務包含了若干個資料庫操作,這些操作通常都會對資料庫產生變化。值得一提的是,多個事務之間是互不影響,獨立執行的,事務裡的各個操作最終都得以持久化。

事務一個很重要的特性是:"all-or-nothing"

通俗來講,事務是對資料的一個邏輯操作,事務內的各個單元操作要麼完整執行成功,要麼全部都不執行。

因此,資料庫的事務機制為一系列的資料庫操作提供了相對獨立(隔離)的執行環境,保證了資料的一致性,並且使得資料庫能被正確的恢復過來。

0、事務的特性

這一小節講的是資料庫事務四個經典的特性:「ACID」

讀者如果熟悉,可以選擇性略過。

「ACID」是四個單詞的首字母縮寫,首次被提出來是在1983年。

與其說這是事務的四大特性,倒不如認為是一種設計思想,這種思想在以後影響了資料庫系統開發的方方面面。

Atomicity - 原子性

這個特性在上文中也有提到過,即 "all or nothing":一個事務中的所有操作都順利完成了,才可以認為這個事務是成功的,否則,但凡有一個操作失敗了,就意味著整個事務的失敗,資料庫的相關資料狀態也就不會被改變。

值得一提的是,操作失敗不僅僅是業務或者編碼層面,也包括一些外部因素,比如斷點,磁碟故障等。

Consistency - 一致性

講到事務的一致性,通常指的是資料庫相關的約束條件,包括資料庫自帶的約束以及使用者自定義的約束,比如資料的唯一性(unique),資料的級聯相關性(cascade),觸發器(trigger)等。

這一系列的約束旨在保證事務能將資料庫從一個合理的狀態轉移到另一個合理的狀態,而所寫入的資料也都是遵從上述定義的約束條件。

當然,這裡所提到的一致性,沒法從更高的應用層面(通常是業務層)保證資料一致性,僅僅是從程式上規避編碼錯誤導致對約束條件的破壞。比如插入了一個已經存在的主鍵值,或者沒給一個NOT NULL欄位賦上一個合理的值……

Durability - 永續性

這個特性比較好理解,一個事務一旦被成功提交,那麼所操作的資料將被永久存放在磁盤裡了。持久化到磁碟,也是為了防止斷電導致資料丟失,直接放記憶體中,顯然不是明智的選擇。

Isolation - 隔離性

這個特性是最晚被提出來的,Jim Gray最早只提出上述的三個特性,後來Andreas Reuter 和 Theo Härder在此基礎上,增加了隔離性。

隔離性的提出,是為了解決事務併發的問題,是指併發的兩個事務的執行互不干擾,一個事務不能看到其他事務執行過程的中間狀態。

從隔離性的使用場景能感受到,提出此特性也是有時代背景的,也是人們為了解決併發控制問題而提出的對策。

1、何為「分散式事務」

在事務的基礎上,加上了「分散式」,這意味著要在分散式的環境下滿足事務的ACID特性。而分散式意味著網路可能是異構的,作業系統也不盡相同,資料庫系統也不只在一臺主機上了。

實際上,隨著資料規模的急劇增長,單體資料庫無法承載如此海量的資料,需要對原有資料進行合理的分庫分表,此外,對於近來盛行的「微服務」架構,每個服務獨立部署,有獨立的資料庫,也不得不面臨分散式事務的問題。

總之,隨著分散式架構的大規模運用,分散式事務是不可迴避的問題。

如果我們將「分散式事務」認為是一種事務,那麼又該如何設計,使其滿足ACID四大特性呢?

2、兩段式提交

最經典的分散式事務解決方法就是“兩段式提交(two-phase commit)”。

在兩段式提交過程中,涉及兩類角色,協調者(Coordinator)參與者(Participants)

顧名思義,“兩段式提交”將事務的提交過程分為了兩個階段:

  • 第一個階段:預提交階段,也可以稱之為投票階段。在這個階段,協調者詢問所有的參與者是否已準備好提交事務,參與者通常都會給出一個“YES or NO”的回答,即我們認為的投票過程。根據事務的Atomic特性,但凡有一個參與者Say NO,那麼協調者就認為這個事務提交是失敗的。只有全部參與者給出“YES”的回答,才能讓事務順利提交。

  • 第二個階段:提交決定階段。協調者根據上一個階段的投票結果決定是Commit還是Abort,這個決定是全域性性的,會通知到所有的參與者執行最終的決定,並回傳一個ack確認資訊。

值得注意的是,在進入兩段式提交過程期間,所有的參與者不能臨時改變主意,即投票不可反悔,下面是兩段式提交過程的示意圖:

two-phase commit protocol

仔細想想,兩段提交協議也並不完美。

兩段式提交時一個典型的中心化架構協議,被指定為協調者的節點如果沒有做高可用措施的話,協調者的宕機意味著事務再也無法正常提交了。與此同時,各個節點(包括協調者和參與者)只有在永無故障的前提下(雖然絕大多數的時候是OK的),才能使得事務順利提交。

這些假設條件無疑是嚴苛的,因為要達到強一致性的目的。

不過,這也使得兩段式提交協議在某些時候是比較脆弱的。兩段式提交協議的效能比較差, 訊息互動多,且受最慢節點影響。

基於兩段式提交的分散式事務在提交事務時需要在多個節點之間進行協調,最大限度地推後了提交事務的時間點。
客觀上延長了事務的執行時間,同時也會提高事務在訪問共享資源時發生衝突和死鎖的機率。隨著資料庫節點的增多,這種趨勢會越來越嚴重,從而成為系統在資料庫層面上水平伸縮的"枷鎖"。 這是很多Sharding系統不採用分散式事務的主要原因。

就好比你在旅遊網站上訂了一個行程,除了生成一個出行訂單外,還需要去航空公司為你預訂相應的機票,還要去酒店為你預訂每天的房間。如果根據兩段式提交協議,只有當訂單、機票和房間都準備好了,這個行程才算預訂好了,這在顯然是不合理的。對於這種長生命週期的分散式事務就不適合兩段式提交。

3、三段式提交

顯然,三段式提交協議是基於兩段式提交而生的,為了解決兩段式提交帶來的阻塞等待問題,三段式提交引入TIMEOUT機制,可在超時後自動釋放資源。

和兩段式提交一樣,三段式提交協議有兩類角色,協調者(Coordinator)參與者(Participants),由三個階段構成。

  • 第一個階段:詢問階段。協調者詢問每個參與者是否可以進行提交,這時候會出現多種情況。參與者明確自己是否能提交,可以給出“YES or NO”的準確回答,也有可能因為各種因素,導致不能確定,直到此次詢問超時,返回“NO”。

  • 第二個階段:預提交階段。根據上階段得到的應答,協調者決定事務Commit or Abort,將投票最終結果傳送給各個參與者,參與者收到此決定後再繼續下面的操作,只不過到了此階段,雙方都有超時機制了。協調者也有可能因為各種原因不能及時做出決定,超時後就自動給出了Abort決定,與此同時,參與者收到了協調者的決定,需要回傳ACK資訊以確定,如果沒有在規定的時間視窗內確認,協調者認為事務應該Abort。

  • 第三個階段:正式提交階段。在上一個階段,各個參與者已經收到了事務Commit or Abort的確認資訊,其實這個階段可以認為是一個二次確認階段,協調者會發送一個DoCommit指令,參與者才真正開始進行事務的操作,並給協調者回復一個ACK。如果此時協調者接收ACK超時,協調者也會Abort整個事務。值得注意的是,如果協調者本身傳送DoCommit就超時了,參與者也不會直接Abort事務,而是按照第二個階段的結果執行。

下面附上兩段式提交與三段式提交的框架圖:

4、TCC

TCC包含了三個階段:Try,Confirm,Cancel,因此而得名「TCC」。

TCC的概念屬於國產,因為支付寶的技術佈道而廣為人知。

其實,TCC算是一種程式設計模型,通常被理解為是一種柔性事務解決方案。

  • Try,嘗試執行業務。完成所有業務檢查,預留相應的業務資源。

  • Confirm,如果Try階段執行成功,則此階段利用Try階段預留的資源,不再進行業務檢查,而是執行真正的業務提交。並且Confirm階段的操作滿足冪等性,以便支援重試。這個階段有一個假設:只要Try階段成功,那麼Confirm階段一定成功。

  • Cancel,此階段是發生在Try階段出現失敗的時候,回滾之前的操作,釋放Try階段預留的業務資源,同樣也滿足冪等性。

借用一個例子:

使用者下完訂單後,使用紅包帳戶和資金帳戶來付款,紅包帳戶服務和資金帳戶服務在不同的系統中。一個是CapitalTradeOrderService,代表著資金帳戶服務,另一個是RedPacketTradeOrderService,代表著紅包帳戶服務。
下完訂單後,訂單狀態為DRAFT,在TRY階段,訂單支付服務將訂單狀態變成PAYING,同時遠端呼叫紅包帳戶服務和資金帳戶服務,將付款方的餘額減掉(預留業務資源);
如果在Try階段,任何一個服務失敗,將會呼叫這些服務對應的cancel方法,訂單支付服務將訂單狀態變成PAY_FAILED,同時遠端呼叫紅包帳戶服務和資金帳戶服務,將付款方餘額減掉的部分增加回去;
如果Try階段正常完成,則進入Confirm階段,在Confirm階段,訂單支付服務將訂單狀態變成CONFIRMED,同時遠端呼叫紅包帳戶服務和資金帳戶服務對應的Confirm方法,將收款方的餘額增加。

總結

本文對事務進行了簡單介紹,重點分析了事務的ACID四大特性,接著介紹分散式事務的解決方案。

希望對你有所幫助!

THANKS!