現今互聯網界,分布式系統和微服務架構盛行。一個簡單操作,在服務端非常可能是由多個服務和數據庫實例協同完成的。在一致性要求較高的場景下,多個獨立操作之間的一致性問題顯得格外棘手。基於水平擴容能力和成本考慮,傳統的強一致的解決方案(e.g.單機事務)紛紛被拋棄。其理論依據就是響當當的CAP原理。往往為了可用性和分區容錯性,忍痛放棄強一致支持,轉而追求最終一致性。分布式系統的特性在分布式系統中,同時滿足... | ||||
現今互聯網界,分布式系統和微服務架構盛行。一個簡單操作,在服務端非常可能是由多個服務和數據庫實例協同完成的。在一致性要求較高的場景下,多個獨立操作之間的一致性問題顯得格外棘手。 基於水平擴容能力和成本考慮,傳統的強一致的解決方案(e.g.單機事務)紛紛被拋棄。其理論依據就是響當當的CAP原理。往往為了可用性和分區容錯性,忍痛放棄強一致支持,轉而追求最終一致性。
分布式系統的特性 在分布式系統中,同時滿足CAP定律中的一致性 Consistency、可用性 Availability和分區容錯性 Partition Tolerance三者是不可能的。在絕大多數的場景,都需要犧牲強一致性來換取系統的高可用性,系統往往只需要保證最終一致性。 CAP理解:
ACID理解:
分布式事務的基本介紹 分布式事務服務(Distributed Transaction Service,DTS)是一個分布式事務框架,用來保障在大規模分布式環境下事務的最終一致性。 CAP理論告訴我們在分布式存儲系統中,最多只能實現上面的兩點。而由於當前的網絡硬件肯定會出現延遲丟包等問題,所以分區容忍性是我們必須需要實現的,所以我們只能在一致性和可用性之間進行權衡。 為了保障系統的可用性,互聯網系統大多將強一致性需求轉換成最終一致性的需求,並通過系統執行冪等性的保證,保證數據的最終一致性。 數據一致性理解:
常用的分布式技術說明 1. 本地消息表 這種實現方式的思路是源於ebay,其基本的設計思想是將遠程分布式事務拆分成一系列的本地事務。 舉個經典的跨行轉賬的例子來描述。 第一步偽代碼如下,扣款1W,通過本地事務保證了憑證消息插入到消息表中。
第二步,通知對方銀行賬戶上加1W了,通常采用兩種方式:
2. 消息中間件 非事務性的消息中間件 還是以上述提到的跨行轉賬為例,我們很難保證在扣款完成之后對MQ投遞消息的操作就一定能成功。這樣一致性似乎很難保證。
我們來分析下可能的情況:
從上面分析的幾種情況來看,基本上能保證發送者發送消息的可靠性。我們再來分析下消費者端面臨的問題:
支持事務的消息中間件 除了上面介紹的通過異常捕獲和回滾的方式外,還有沒有其他的思路呢? 阿里巴巴的RocketMQ中間件就支持一種事務消息機制,能夠確保本地操作和發送消息達到本地事務一樣的效果。
但是如果第三階段的確認消息發送失敗了怎么辦?RocketMQ會定期掃描消息集群中的事物消息,如果發現了prepare狀態的消息,它會向消息發送者確認本地事務是否已執行成功,如果成功是回滾還是繼續發送確認消息呢。RocketMQ會根據發送端設置的策略來決定是回滾還是繼續發送確認消息。這樣就保證了消息發送與本地事務同時成功或同時失敗。 目前主流的開源MQ(ActiveMQ、RabbitMQ、Kafka)均未實現對事務消息的支持,比較遺憾的是,RocketMQ事務消息部分的代碼也並未開源,需要自己去實現。
理解2PC和3PC協議 為了解決分布式一致性問題,前人在性能和數據一致性的反反復復權衡過程中總結了許多典型的協議和算法。其中比較著名的有二階提交協議(2 Phase Commitment Protocol),三階提交協議(3 Phase Commitment Protocol)。 2PC 分布式事務最常用的解決方案就是二階段提交。在分布式系統中,每個節點雖然可以知曉自己的操作時成功或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事務的ACID特性,需要引入一個作為協調者的組件來統一掌控所有參與者節點的操作結果並最終指示這些節點是否要把操作結果進行真正的提交。 因此,二階段提交的算法思路可以概括為:參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作。 所謂的兩個階段是指:第一階段:准備階段(投票階段)和第二階段:提交階段(執行階段)。 第一階段:投票階段 該階段的主要目的在於打探數據庫集群中的各個參與者是否能夠正常的執行事務,具體步驟如下: 1. 協調者向所有的參與者發送事務執行請求,並等待參與者反饋事務執行結果。 2. 事務參與者收到請求之后,執行事務,但不提交,並記錄事務日志。 3. 參與者將自己事務執行情況反饋給協調者,同時阻塞等待協調者的后續指令。 第二階段:事務提交階段 在第一階段協調者的詢盤之后,各個參與者會回復自己事務的執行情況,這時候存在三種可能: 1. 所有的參與者回復能夠正常執行事務。 2. 一個或多個參與者回復事務執行失敗。 3. 協調者等待超時。 對於第一種情況,協調者將向所有的參與者發出提交事務的通知,具體步驟如下: 1. 協調者向各個參與者發送commit通知,請求提交事務。 2. 參與者收到事務提交通知之后,執行commit操作,然后釋放占有的資源。 3. 參與者向協調者返回事務commit結果信息。 對於第二、三種情況,協調者均認為參與者無法正常成功執行事務,為了整個集群數據的一致性,所以要向各個參與者發送事務回滾通知,具體步驟如下: 1. 協調者向各個參與者發送事務rollback通知,請求回滾事務。 2. 參與者收到事務回滾通知之后,執行rollback操作,然后釋放占有的資源。 3. 參與者向協調者返回事務rollback結果信息。 兩階段提交協議解決的是分布式數據庫數據強一致性問題,其原理簡單,易於實現,但是缺點也是顯而易見的,主要缺點如下:
3PC 針對兩階段提交存在的問題,三階段提交協議通過引入一個“預詢盤”階段,以及超時策略來減少整個集群的阻塞時間,提升系統性能。三階段提交的三個階段分別為:can_commit,pre_commit,do_commit。 第一階段:can_commit 該階段協調者會去詢問各個參與者是否能夠正常執行事務,參與者根據自身情況回復一個預估值,相對於真正的執行事務,這個過程是輕量的,具體步驟如下: 1. 協調者向各個參與者發送事務詢問通知,詢問是否可以執行事務操作,並等待回復。 2. 各個參與者依據自身狀況回復一個預估值,如果預估自己能夠正常執行事務就返回確定信息,並進入預備狀態,否則返回否定信息。 第二階段:pre_commit 本階段協調者會根據第一階段的詢盤結果采取相應操作,詢盤結果主要有三種: 1. 所有的參與者都返回確定信息。 2. 一個或多個參與者返回否定信息。 3. 協調者等待超時。 針對第一種情況,協調者會向所有參與者發送事務執行請求,具體步驟如下: 1. 協調者向所有的事務參與者發送事務執行通知。 2. 參與者收到通知后,執行事務,但不提交。 3. 參與者將事務執行情況返回給客戶端。 在上面的步驟中,如果參與者等待超時,則會中斷事務。 針對第二、三種情況,協調者認為事務無法正常執行,於是向各個參與者發出abort通知,請求退出預備狀態,具體步驟如下: 1. 協調者向所有事務參與者發送abort通知 2. 參與者收到通知后,中斷事務 第三階段:do_commit 如果第二階段事務未中斷,那么本階段協調者將會依據事務執行返回的結果來決定提交或回滾事務,分為三種情況: 1. 所有的參與者都能正常執行事務。 2. 一個或多個參與者執行事務失敗。 3. 協調者等待超時。 針對第一種情況,協調者向各個參與者發起事務提交請求,具體步驟如下: 1. 協調者向所有參與者發送事務commit通知。 2. 所有參與者在收到通知之后執行commit操作,並釋放占有的資源。 3. 參與者向協調者反饋事務提交結果。 針對第二、三種情況,協調者認為事務無法正常執行,於是向各個參與者發送事務回滾請求,具體步驟如下: 1. 協調者向所有參與者發送事務rollback通知。 2. 所有參與者在收到通知之后執行rollback操作,並釋放占有的資源。 3. 參與者向協調者反饋事務提交結果。 在本階段如果因為協調者或網絡問題,導致參與者遲遲不能收到來自協調者的commit或rollback請求,那么參與者將不會如兩階段提交中那樣陷入阻塞,而是等待超時后繼續commit。相對於兩階段提交雖然降低了同步阻塞,但仍然無法避免數據的不一致性。
Reference https://zhuanlan.zhihu.com/p/25933039 http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency http://blog.csdn.net/jasonsungblog/article/details/49017955 http://blog.csdn.net/suifeng3051/article/details/52691210 https://my.oschina.net/wangzhenchao/blog/736909 轉載請並標注: “本文轉載自 linkedkeeper.com (文/張松然)” |