接受「不完美」:分散式事務學習總結

作為一個前端專業的人來說,對於事務的理解,一直停留在「要麼都成功,要麼都不成功」的小白階段。既然自己將2018年定義為」深入理解「的一年,那麼就從深入理解事務開始吧。

什麼是事務?

正如文章開頭所說的:事務是一系列的動作,這些動作必須全部完成,如果有一個失敗,那麼事務就會回滾到最開始的狀態,彷彿什麼都沒發生過一樣。在企業級應用的開發過程中,事務管理是必不可少的技術,用來確保數據的完整性和一致性。

事務有四個特性,也就是經常被提到的ACID:

  • 原子性(Atomicity):所謂的原子性就是說,在整個事務中的所有操作,要麼全部完成,要麼全部不做,沒有中間狀態。對於事務在執行中發生錯誤,所有的操作都會被回滾,整個事務就像從沒被執行過一樣。
  • 一致性(Consistency):事務的執行必須保證系統的一致性,就拿轉賬為例,A有500元,B有300元,如果在一個事務里A成功轉給B50元,那麼不管並發多少,不管發生什麼,只要事務執行成功了,那麼最後A賬戶一定是450元,B賬戶一定是350元。
  • 隔離性(Isolation):所謂的隔離性就是說,事務與事務之間不會互相影響,一個事務的中間狀態不會被其他事務感知。
  • 持久性(Durability):所謂的持久性,就是說一單事務完成了,那麼事務對數據所做的變更就完全保存在了資料庫中,即使發生停電,系統宕機也是如此。

上面我們說到的事務,也可以稱為是」本地事務「。目前許多框架,都能夠很方便的支持本地事務。比如Spring Boot,只需要在方法前加上」@Transaction「的註解,就可以愉快的使用事務了。

但是,事務到此未知就結束了嗎?不是的,隨著企業應用越來越複雜,應用的架構也從單體架構演變到了SOA,還有現在炙手可熱的微服務。這時候,又出現了分散式事務的概念。

分散式事務

分散式事務,簡單來說就是指對資料庫的處理操作分布在不同的節點之上,而且操作的數據,分布於不同的資料庫。分散式事務,需要保證不同資料庫的數據一致性。

分散式事務產生的原因

資料庫分庫分表

處於數據量或者數據隔離的考慮,實際開發中需要進行分庫分表。原來一個庫現在變成了多個庫,這時候要保證數據一致性,就要用到分散式事務。

應用SOA化

所謂的SOA話,就是業務的服務化。比如原來單機支撐了整個電商網站,現在對整個網站進行拆解,分離出了訂單中心、用戶中心、庫存中心。對於訂單中心,有專門的資料庫存儲訂單信息,用戶中心也有專門的資料庫存儲用戶信息,庫存中心也會有專門的資料庫存儲庫存信息。這時候如果要同時對訂單和庫存進行操作,那麼就會涉及到訂單資料庫和庫存資料庫,為了保證數據一致性,就需要用到分散式事務。

CPA與BASE

說到分散式事務,就離不開CPA原則與BASE方案。

CPA

CPA指的是,在一個分散式系統中,一致性(C)、可用性(A)、分區容錯性(P),三者不可兼得。CPA是NoSQL資料庫的基石。

  • 一致性:在分散式系統中的所有數據備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的數據副本)
  • 可用性:在集群中一部分節點故障後,集群整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性)
  • 分區容錯性:以實際效果而言,分區相當於對通信的時限要求。系統如果不能在時限內達成數據一致性,就意味著發生了分區的情況,必須就當前操作在C和A之間做出選擇。

CAP理論就是說在分散式存儲系統中,最多只能實現上面的兩點。而由於當前的網路硬體肯定會出現延遲丟包等問題,所以分區容忍性是我們必須需要實現的。所以我們只能在一致性和可用性之間進行權衡,沒有NoSQL系統能同時保證這三點。

BASE

BASE就是為了解決關係資料庫強一致性引起的問題而引起的可用性降低而提出的解決方案。

BASE是下面三個術語的縮寫:

  • 基本可用(Basically Available)
  • 軟狀態(Soft state)
  • 最終一致(Eventually consistent)

常見的分散式事務解決方案

兩階段提交

兩階段提交(Two Phase Commit, 2PC), 具有強一致性, 是CP系統的一種典型實現,常見的標準是XA,JTA等。例如Oracle的資料庫支持XA。

下面是兩階段提交的示意圖:

圖的上半是兩階段提交成功的演示, 下半是兩階段提交失敗的演示。

兩階段提交目前並不是主流的解決方案,其主要原因是:協調者需要等待所有參與者發出yes請求,或者一個參與者發出no請求後,才能執行提交或者終端操作。這會造成長時間鎖住多個資源,造成性能瓶頸。如果參與者有一個耗時長的操作, 性能損耗會更明顯;還有一個缺點,就是實現複雜,不利於系統的擴展。

TCC(Try-Confirm-Cancel)

TCC, 是基於補償型事務的AP系統的一種實現, 具有最終一致性。所謂的TCC編程模式,也是兩階段提交的一個變種。TCC提供了一個編程框架,將整個業務邏輯分為三塊:Try、Confirm和Cancel三個操作。以在線下單為例,Try階段會去扣庫存,Confirm階段則是去更新訂單狀態,如果更新訂單失敗,則進入Cancel階段,會去恢復庫存。總之,TCC就是通過代碼人為實現了兩階段提交,不同的業務場景所寫的代碼都不一樣,複雜度也不一樣,因此,這種模式並不能很好地被複用。

非同步確保型

通過將一系列同步的事務操作變為基於消息執行的非同步操作, 避免了分散式事務中的同步阻塞操作的影響。基於消息執行就是基於消息中間件的兩階段提交,本質上是對消息中間件的一種特殊利用,它是將本地事務和發消息放在了一個分散式事務里,保證要麼本地操作成功成功並且對外發消息成功,要麼兩者都失敗,開源的RocketMQ就支持這一特性,具體原理如下:

執行步驟如下:

  1. MQ發送方發送遠程事務消息到MQ Server;
  2. MQ Server給予響應, 表明事務消息已成功到達MQ Server.
  3. MQ發送方Commit本地事務.
  4. 若本地事務Commit成功, 則通知MQ Server允許對應事務消息被消費; 若本地事務失敗, 則通知MQ Server對應事務消息應被丟棄.
  5. 若MQ發送方超時未對MQ Server作出本地事務執行狀態的反饋, 那麼需要MQ Servfer向MQ發送方主動回查事務狀態, 以決定事務消息是否能被消費.
  6. 當得知本地事務執行成功時, MQ Server允許MQ訂閱方消費本條事務消息.

需要額外說明的一點, 就是事務消息投遞到MQ訂閱方後, 並不一定能夠成功執行. 需要MQ訂閱方主動給予消費反饋(ack)

  • 如果MQ訂閱方執行遠程事務成功, 則給予消費成功的ack, 那麼MQ Server可以安全將事務消息移除;
  • 如果執行失敗, MQ Server需要對消息重新投遞, 直至消費成功.
  • 根據業務邏輯的具體實現不同,還可能需要對消息中間件增加消息不重複, 不亂序等其它要求.

此方案適用於執行周期較長,實時性要求不高的場景。

最大努力通知型

這是分散式事務中要求最低的一種, 也可以通過消息中間件實現, 與前面非同步確保型操作不同的一點是, 在消息由MQ Server投遞到消費者之後, 允許在達到最大重試次數之後正常結束事務.這種方案適用於交易結果消息的通知等

微服務的事務

最近兩年,微服務的呼聲越來越高,不可避免的,微服務也會面臨事務的困擾。

  • 首先,對於微服務架構來說,數據訪問變得更加複雜,這是因為數據都是微服務私有的,唯一可訪問的方式就是通過 API。這種打包數據訪問方式使得微服務之間松耦合,並且彼此之間獨立,更容易進行性能擴展。
  • 其次,不同的微服務經常使用不同的資料庫。應用會產生各種不同類型的數據,關係型資料庫並不一定是最佳選擇基於微服務的應用一般都使用 SQL 和 NoSQL 結合的模式。但是這些非關係型數據大多數並不支持 2PC。

總結

事務,尤其是分散式事務,是一個很大的話題,除了上述列出的幾種解決方案,根據不同的業務要求,還有許多其他的解決方案。按照控制力度,分散式事務分為部分控制和完全控制兩種:

  • 部分控制就是各種變種的兩階段提交,包括上面提到的非同步確保型、TCC模式
  • 完全控制就是完全實現兩階段提交。部分控制的好處是並發量和性能很好,缺點是數據一致性減弱了,完全控制則是犧牲了性能,保障了一致性。

    具體用哪種方式,最終還是取決於業務場景。

推薦閱讀:

集群資源調度系統設計架構總結
分散式系統設計:單點模式之挎斗模式
快速打造分散式深度學習訓練平台
閱讀筆記:Scaling Memcache at Facebook

TAG:分散式事務 | 分散式系統 | 微服務架構 |