sql
一個簡單的例子,電商系統中,下單接口,通常會有扣庫存,扣積分,而後生成訂單。而通常來講,這三個系統都是不一樣的服務,咱們本地不能控制其餘服務的事務,此時若是訂單服務發生了錯誤進行了回滾,但遠程的服務,如扣庫存已經調用完成,不能進行回滾了。 也就是說下單接口的成功與否,不只取決於本地的 db 操做,並且依賴第三方系統的結果, 這時候分佈式事務就保證這些操做要麼所有成功,要麼所有失敗。本質上來講,分佈式事務就是爲了保證不一樣數據庫的數據一致性。 數據庫
在講分佈式事務以前,先回顧下本地事務的知識點。服務器
嚴格意義上的事務實現應該是具有原子性( Atomicity )、一致性( Consistency )、隔離性( Isolation )和持久性(Durability),簡稱 ACID。 網絡
原子性:一系列操做總體不可拆分,要麼都執行,要麼都不執行。併發
一致性: 事務的執行不能破壞數據庫數據的完整性和一致性,一個事務在執行以前和執行以後,數據庫都必須處於一致性狀態 異步
隔離性:事務之間相互隔離, 指的是多個事務併發執行的時候不會互相干擾,即一個事務內部的數據對於其餘事務來講是隔離的。 分佈式
持久性: 一旦事務提交,那麼它對數據庫中的對應數據的狀態的變動就會永久保存到數據庫中 學習
通俗意義上事務就是爲了使得一些更新操做要麼都成功,要麼都失敗。 spa
隔離性中還有一個隔離級別的概念,總共有4個事務隔離級別,不一樣的隔離級別對事務的處理不一樣,分別是:未提交讀,已提交讀, 可重複讀,串行化。這裏面又牽扯到三個概念,髒讀、不可重複讀 ,幻讀,咱們先理解這三個概念設計
髒讀:所謂的髒讀,其實就是讀到了別的事務回滾前的髒數據
不可重複讀:當前事務先進行了一次數據讀取,而後再次讀取到的數據是別的事務修改爲功的數據,致使兩次讀取到的數據不匹配
幻讀:當前事務讀第一次取到的數據比後來讀取到數據條目少
而事務的隔離級別其實就是如何避免這三種
未提交讀: 該隔離級別容許髒讀取,其隔離級別最低,也就是啥都沒避免。
已提交讀 :一個事務能夠讀取已提交的事務,保證了一個事務不會讀到另外一個並行事務已修改但未提交的數據。可是不保證可重複讀,也就是不保證屢次讀取數據都相同。
可重複讀:保證屢次讀取一個數據時都跟開始讀取的時候同樣, 所以該事務級別禁止不可重複讀取和髒讀取,可是有可能出現幻讀數據。
串行化: 是最嚴格的事務隔離級別,它要求全部事務被串行執行,即事務只能一個接一個的進行處理,不能併發執行。 該隔離級別能防止髒讀、不可重複讀、幻讀。
Mysql默認級別是可重複讀,在編寫代碼時是能夠進行設置的。
而在Spring中七種事務傳播行爲事務的傳播行爲概念:
PROPAGATION_REQUIRED: 若是當前存在事務,則加入該事務,若是當前不存在事務,則建立一個新的事務。
PROPAGATION_SUPPORTS: 若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。
PROPAGATION_MANDATORY: 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。
PROPAGATION_REQUIRES_NEW: 從新建立一個新的事務,若是當前存在事務,延緩當前的事務。
PROPAGATION_NOT_SUPPORTED: 以非事務的方式運行,若是當前存在事務,暫停當前的事務。
PROPAGATION_NEVER: 老是非事務地執行,若是存在一個活動事務,則拋出異常。
PROPAGATION_NESTED: 若是沒有,就新建一個事務;若是有,就在當前事務中嵌套其餘事務。
這裏有個點,Spring的事務實現是經過代理類實現的,因此同一個對象內事務調用是默認失效的, 默認只有在外部調用事務纔會生效 。
回顧完本地事務,讓咱們回到分佈式事務的學習,分佈式事務的實現是創建在不少概念之上的,讓咱們先來理解下基礎概念吧。
CAP是分佈式當中一個很是重要的理論,指的是在一個分佈式系統中一致性 (Consistency)、可用性 (Availability)、分區容錯性(Partition tolerance),三者不可得兼。
一致性:在分佈式系統中的全部數據備份,在同一時刻是否一樣的值。(等同於全部節點訪問同一份最新的數據副本)
可用性:在集羣中一部分節點故障後,集羣總體是否還能響應客戶端的讀寫請求。(對數據更新具有高可用性)
分區容錯性:分佈式系統在遇到任何網絡分區故障的時候,仍然須要可以保證對外提供知足一致性和可用性的服務,除非整個網絡環境都發生故障。
CAP理論是指一個分佈式系統不可能同時知足一致性,可用性和分區容錯性這個三個基本需求,最多隻能同時知足其中兩項。
舉個例子:有A、B、C三個服務,都保存一份數據是7,而當A服務將這個數據改爲8,同步到B時,正常,但同步至C時出現了異常,此時C仍然是7,若是此時依舊要保持一致性,那麼C服務就不能可用。
放棄P(CA):若是但願可以避免系統出現分區容錯性問題,一種較爲簡單的作法就是將全部的數據(或者是與事物先相關的數據)都放在一個分佈式節點上,這樣雖然沒法保證100%系統不會出錯,但至少不會碰到因爲網絡分區帶來的負面影響。可是這樣其實就不是分佈式系統了,
放棄A(CP):其作法是一旦系統遇到網絡分區或其餘故障時,那受到影響的服務須要等待必定的時間,應用等待期間系統沒法對外提供正常的服務,即不可用
放棄C(AP):這裏說的放棄一致性,並非徹底不須要數據一致性,是指放棄數據的強一致性,保留數據的最終一致性。
大多數時候咱們是選擇AP,也就是選擇放棄一致性,可是這不是絕對的,在一些業務中,例如,轉帳業務,是放棄了可用性。
BASE 理論指的是基本可用 Basically Available,軟狀態 Soft State,最終一致性 Eventual Consistency,核心思想是即使沒法作到強一致性,但應該採用適合的方式保證最終一致性。
BA: Basically Available 基本可用,分佈式系統在出現故障的時候,容許損失部分可用性,即保證核心可用。
S:Soft State 軟狀態,容許系統存在中間狀態,而該中間狀態不會影響系統總體可用性。
E: Eventual Consistency 最終一致性,系統中的全部數據副本通過必定時間後,最終可以達到一致的狀態。
這裏須要解釋下強一致性,弱一致性和最終一致性。
強一致性:任何一次讀都能讀到某個數據的最近一次寫的數據。系統中的全部進程,看到的操做順序,都和全局時鐘下的順序一致。簡言之,在任意時刻,全部節點中的數據是同樣的。
弱一致性: 數據更新後,若是能容忍後續的訪問只能訪問到部分或者所有訪問不到,則是弱一致性。
最終一致性: 不保證在任意時刻任意節點上的同一份數據都是相同的,可是隨着時間的遷移,不一樣節點上的同一份數據老是在向趨同的方向變化。簡單說,就是在一段時間後,節點間的數據會最終達到一致狀態。
上面都是一些理論,用這些理論做爲指導,分佈式事務有如下一些解決方案
兩階段提交,顧名思義就是要分兩步提交。存在一個負責協調各個本地資源管理器的事務管理器,本地資源管理器通常是由數據庫實現,事務管理器在第一階段的時候詢問各個資源管理器是否都就緒?若是收到每一個資源的回覆都是 yes,則在第二階段提交事務,若是其中任意一個資源的回覆是 no, 則回滾事務。
階段1:提交事務請求
事務管理器向全部本地資源管理器發起請求,詢問是不是 ready 狀態,全部參與者都將本事務可否成功的信息反饋發給協調者;
事務詢問:協調者向全部的參與者發送事務內容,詢問是否能夠執行事務提交操做,並開始等待各參與者的響應
執行事務:各參與者節點執行事務操做,並將Undo和Redo信息記入事務日誌中
若是參與者成功執事務操做,就反饋給協調者Yes響應,表示事物能夠執行,若是沒有成功執行事務,就反饋給協調者No響應,表示事務不能夠執行
二階段提交一些的階段也被稱爲投票階段,即各參與者投票票代表是否能夠繼續執行接下去的事務提交操做
二階段: 執行事務提交
假如協調者從全部的參與者或得反饋都是Yes響應,那麼就會執行事務提交。
發送提交請求:協調者向全部參與者節點發出Commit請求
事務提交:參與者接受到Commit請求後,會正式執行事務提交操做,並在完成提交以後放棄整個事務執行期間佔用的事務資源
反饋事務提交結果:參與者在完成事物提交以後,向協調者發送ACK消息
完成事務:協調者接收到全部參與者反饋的ACK消息後,完成事務
中斷事務
假如任何一個參與者向協調者反饋了No響應,或者在等待超時以後,協調者尚沒法接收到全部參與者的反饋響應,那麼就中斷事務。
發送回滾請求:協調者向全部參與者節點發出Rollback請求
事務回滾:參與者接收到Rollback請求後,會利用其在階段一種記錄的Undo信息執行事物回滾操做,並在完成回滾以後釋放事務執行期間佔用的資源。
反饋事務回滾結果:參與則在完成事務回滾以後,向協調者發送ACK消息
中斷事務:協調者接收到全部參與者反饋的ACk消息後,完成事務中斷
這種解決方案優勢是實現簡單,缺點也很明顯:
同步阻塞:當參與事務者存在佔用公共資源的狀況,其中一個佔用了資源,其餘事務參與者就只能阻塞等待資源釋放,處於阻塞狀態。
單點故障:一旦事務管理器出現故障,整個系統不可用
數據不一致:在階段二,若是事務管理器只發送了部分 commit 消息,此時網絡發生異常,那麼只有部分參與者接收到 commit 消息,也就是說只有部分參與者提交了事務,使得系統數據不一致。
不肯定性:當協事務管理器發送 commit 以後,而且此時只有一個參與者收到了 commit,那麼當該參與者與事務管理器同時宕機以後,從新選舉的事務管理器沒法肯定該條消息是否提交成功。
還有3PC提交,是對2PC提交作了一些改進
與兩階段提交不一樣的是,三階段提交有兩個改動點。引入超時機制。同時在協調者和參與者中都引入超時機制。在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段以前各參與節點的狀態是一致的。
三階段提交就有CanCommit、PreCommit、DoCommit三個階段。
TCC 指的是Try - Confirm - Cancel
。
Try 指的是預留,即資源的預留和鎖定,注意是預留。
Confirm 指的是確認操做,這一步其實就是真正的執行了。
Cancel 指的是撤銷操做,能夠理解爲把預留階段的動做撤銷了。
TCC 事務機制相比於上面介紹的 XA,解決了其幾個缺點:
解決了協調者單點,由主業務方發起並完成這個業務活動。業務活動管理器也變成多點,引入集羣。
同步阻塞:引入超時,超時後進行補償,而且不會鎖定整個資源,將資源轉換爲業務邏輯形式,粒度變小。
數據一致性,有了補償機制以後,由業務活動管理器控制一致性
TCC(Try Confirm Cancel) Try 階段:嘗試執行,完成全部業務檢查(一致性), 預留必須業務資源(準隔離性) Confirm 階段:確認執行真正執行業務,不做任何業務檢查,只使用 Try 階段預留的業務資源,Confirm 操做知足冪等性。要求具有冪等設計,Confirm 失敗後須要進行重試。 Cancel 階段:取消執行,釋放 Try 階段預留的業務資源 Cancel 操做知足冪等性 Cancel 階段的異常和 Confirm 階段異常處理方案基本上一致。
在 Try 階段,是對業務系統進行檢查及資源預覽,好比訂單和存儲操做,須要檢查庫存剩餘數量是否夠用,並進行預留,預留操做的話就是新建一個可用庫存數量字段,Try 階段操做是對這個可用庫存數量進行操做。 基於 TCC 實現分佈式事務,會將原來只須要一個接口就能夠實現的邏輯拆分爲 Try、Confirm、Cancel 三個接口,因此代碼實現複雜度相對較高。
本地消息表其實就是利用了 各系統本地的事務來實現分佈式事務。
當系統 A 被其餘系統調用發生數據庫表更操做,首先會更新數據庫的業務表,其次會往相同數據庫的消息表中插入一條數據,兩個操做發生在同一個事務中
系統 A 的腳本按期輪詢本地消息往 mq 中寫入一條消息,若是消息發送失敗會進行重試
系統 B 消費 mq 中的消息,並處理業務邏輯。若是本地事務處理失敗,會在繼續消費 mq 中的消息進行重試,若是業務上的失敗,能夠通知系統 A 進行回滾操做
消費者與生成者的接口都要支持冪等
生產者須要額外的建立消息表
須要提供補償邏輯,若是消費者業務失敗,須要生產者支持回滾操做
容錯機制:
步驟 1 失敗時,事務直接回滾
步驟 二、3 寫 mq 與消費 mq 失敗會進行重試
步驟 3 業務失敗系統 B 向系統 A 發起事務回滾操做
此方案的核心是將須要分佈式處理的任務經過消息日誌的方式來異步執行。消息日誌能夠存儲到本地文本、數據庫或消息隊列,再經過業務規則自動或人工發起重試。人工重試更多的是應用於支付場景,經過對帳系統對過後問題的處理。
A 系統先向 mq 發送一條 prepare 消息,若是 prepare 消息發送失敗,則直接取消操做
若是消息發送成功,則執行本地事務
若是本地事務執行成功,則想 mq 發送一條 confirm 消息,若是發送失敗,則發送回滾消息
B 系統按期消費 mq 中的 confirm 消息,執行本地事務,併發送 ack 消息。若是 B 系統中的本地事務失敗,會一直不斷重試,若是是業務失敗,會向 A 系統發起回滾請求
mq 會按期輪詢全部 prepared 消息調用系統 A 提供的接口查詢消息的處理狀況,若是該 prepare 消息本地事務處理成功,則從新發送 confirm 消息,不然直接回滾該消息
該方案與本地消息最大的不一樣是去掉了本地消息表,其次本地消息表依賴消息表重試寫入 mq 這一步由本方案中的輪詢 prepare 消息狀態來重試或者回滾該消息替代。其實現條件與餘容錯方案基本一致。目前市面上實現該方案的只有阿里的 RocketMq。
最大努力通知是最簡單的一種柔性事務,適用於一些最終一致性時間敏感度低的業務,且被動方處理結果 不影響主動方的處理結果。
這個方案的大體意思就是:
系統 A 本地事務執行完以後,發送個消息到 MQ;
這裏會有個專門消費 MQ 的服務,這個服務會消費 MQ 並調用系統 B 的接口;
要是系統 B 執行成功就 ok 了;要是系統 B 執行失敗了,那麼最大努力通知服務就定時嘗試從新調用系統 B, 反覆 N 次,最後仍是不行就放棄。
理論到這裏,下一篇,咱們實戰阿里推出的Seata