不少時候,咱們談概念,會感受看的時候很明白,過幾天就忘光了,緣由就是缺少對應的場景。php
「
mysql
沒有落地支撐的PPT都是耍流氓;sql
沒有代碼支撐的架構都是耍流氓;數據庫
沒有業務場景支撐的應用方案都是耍流氓;緩存
「服務器
----大魏語
微信
本篇咱們從實戰角度分析分佈式事務,因此本文會所有用大白話說清楚。網絡
拷問1:爲何要用分佈式事務?架構
首先,若是一次請求只涉及一個數據庫的表,那麼不存在分佈式的問題。那都叫本地事務(LT)。
app
若是一次請求涉及到兩個DB的表,這時候就要用到分佈式事務(DT)。主流數據庫都支持事務管理。
拷問2:分佈式事務的針對對象是什麼?
DB?
錯,是數據,data。
數據包括:DB、Cache、搜索(ES)。但不少時候,咱們在分佈式事務操做最多的是DB。
拷問3:分佈式事務(DT)使用什麼模型?
CAP是最常被提到的模型。
CAP 理論的定義很簡單,CAP 三個字母分別表明了分佈式系統中三個相互矛盾的屬性:
Consistency (一致性):CAP 理論中的副本一致性特指強一致性;
Availiablity(可用性):指系統在出現異常時已經能夠提供服務;
Tolerance to the Partition of network (分區容忍):指系統能夠對網絡分區。這種異常情 況進行容錯處理;
在真實的業務場景中,CAP沒法同時知足,可以知足兩個就很不錯了。所以,仨字母組合,會有:
CA、AP、CP。在實際業務場景中,只有AP和CP。
分佈式事務選擇AP仍是CP,取決於業務特色,取決於業務容忍度。
網上購物場景用CP。關於這個場景,我仍是用紅帽測試代碼:coolstore:
若是要購物,會涉及到幾個業務模塊呢?大體順序是這樣的:
也就是說,咱們在電商的一次操做,會涉及:看目錄、評分、購物車、減庫存、下單等多個功能模塊操做。不一樣的功能模塊由不一樣的微服務支撐,有不一樣的數據庫。這就要保證CP,即數據一致性和分區容忍性。
咱們再看看一個AP模型的分佈式事務。
大魏常常寫公衆號、發公衆號。這裏面涉及到:
新建圖文消息:
寫公衆號內容:
而後推送給幾千人:
在這個分佈式事務中,其實更強調吞吐量。不少時候大魏晚上發blog後,都須要10分鐘blog才能推送給全部訂閱者,就是由於晚上發blog的太多,吞吐量成了瓶頸。
拷問4:分佈式事務(DT)必定是異步的麼?
錯!
同步異步的都有,不少時候是二者混合的場景。
大魏仍是從實踐角度舉例。在京東買大魏的書100本,選完了下單支付,付錢,那麼買書這件事用的就是同步模型。
在上圖的同步模型中,設計到的模塊至少有:減小商品庫存、創建訂單、前臺支付。
若是到支付頁面,一看:須要這麼多錢,猶豫了,暫時先不支付了。可能過了一個小時候想清楚了,才支付,那這就變成了異步事務。咱們在京東退出支付頁面時,也會提示若是24個小時未支付的話(截圖的時候,已通過了幾分鐘),訂單會自動取消。
若是咱們3個小時以後支付,確定也能支付成功。那這三個小時的時間裏,事務是被保存在消息隊列裏了(MQ)。
因此說,分佈式事務不少時候是同步和異步混合的場景。並且咱們在設計這種場景時,還須要保證業務的吞吐量和相應延遲。
因此說,分佈式事務,有CP和AP兩種常見模型。典型的CP如電商購物,強調一致性。典型的AP模型如發blog、發微信朋友圈。而AP和CP這兩種模型,既有異步,又有同步的。
拷問5:分佈式事務設計的核心要點是什麼?
分佈式事務設計的問題,自己是架構設計的問題。架構設計,分爲:服務和數據兩大塊。不少時候,數據和服務是緊耦合的。所以須要進行拆分。
在微服務架構中,每一個微服務都有本身的DB,其實就是爲了把分佈式事務拆分紅本地事務。
針對紅帽coolstore的demo而言,咱們就是將長事務拆分紅短事務,將短事務拆分紅本地事務。
這樣,每一個本地事務都只操做本身的數據,也就是說DB/緩存/MQ必需要有事務管理能力(這個後面講),本地事務才能實現。但每一個本地事務之間是有關聯的。例如:A(下訂單)->B(減庫存)->C(發貨)。若是A成功,B成功,C成功,那麼整個分佈式事務成功。
那麼,若是C失敗呢?
C會發生回滾,B、A會發生補償。C不須要補償,由於本地事務能夠直接rollback。
咱們看一下MySQL如何實現事務管理(BEGIN 、ROLLBACK、COMMIT):
<?php$dbhost = 'localhost:3306'; // mysql服務器主機地址$dbuser = 'root'; // mysql用戶名$dbpass = '123456'; // mysql用戶名密碼$conn = mysqli_connect($dbhost, $dbuser, $dbpass);if(! $conn ){die('鏈接失敗: ' . mysqli_error($conn));}// 設置編碼,防止中文亂碼mysqli_query($conn, "set names utf8");mysqli_select_db( $conn, 'RUNOOB' );mysqli_query($conn, "SET AUTOCOMMIT=0"); // 設置爲不自動提交,由於MYSQL默認當即執行mysqli_begin_transaction($conn); // 開始事務定義
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(8)")){ mysqli_query($conn, "ROLLBACK"); // 判斷當執行失敗時回滾}
if(!mysqli_query($conn, "insert into runoob_transaction_test (id) values(9)")){ mysqli_query($conn, "ROLLBACK"); // 判斷執行失敗時回滾}mysqli_commit($conn); //執行事務mysqli_close($conn);?>
而Redis事務管理的基礎是:MULTI 、 EXEC 、 DISCARD 和 WATCH。
關於Redis的事務管理,能夠參照這篇文章:
https://cloud.tencent.com/developer/article/1798560
拷問5:分佈式事務框架不少,實際到底哪一種用的多。
談到分佈式事務的架構,有不少:XA、 TCC 、SAGA、 事務消息。咱們不談概念,哪一種用的多呢?
實際應用中,同步主要用saga、異步主要使用事務消息。
saga就是事務補償,這上文已經有所介紹。
MQ事務消息重點再也不是保證全部子事務的原子性,而是保證本地事務和發送MQ消息的原子性。相對於saga,MQ事務消息不用提供補償接口)。
須要注意的是,不是全部的MQ都支持事務消息,目前只有RocketMQ支持。
如下內容節選自:https://juejin.cn/post/6844903951448408071
普通MQ的消息處理流程:
消息生成者發送消息
MQ收到消息,將消息進行持久化,在存儲中新增一條記錄
返回ACK給生產者
MQ push 消息給對應的消費者,而後等待消費者返回ACK
若是消息消費者在指定時間內成功返回ack,那麼MQ認爲消息消費成功,在存儲中刪除消息,即執行第6步;若是MQ在指定時間內沒有收到ACK,則認爲消息消費失敗,會嘗試從新push消息,重複執行四、五、6步驟
MQ刪除消息
事務消息與普通消息的區別就在於消息生產環節,生產者首先預發送一條消息到MQ(這也被稱爲發送half消息)
MQ接受到消息後,先進行持久化,則存儲中會新增一條狀態爲待發送
的消息
而後返回ACK給消息生產者,此時MQ不會觸發消息推送事件
生產者預發送消息成功後,執行本地事務
執行本地事務,執行完成後,發送執行結果給MQ
MQ會根據結果刪除或者更新消息狀態爲可發送
若是消息狀態更新爲可發送
,則MQ會push消息給消費者,後面消息的消費和普通消息是同樣的
RocketMQ的模式有利於異步解耦。
咱們想象一個場景-秒殺系統。
如下內容節選自:https://www.jianshu.com/p/06e9a1d75bd5
流程說明以下:
註冊系統向消息隊列 RocketMQ 發送半事務消息。
1.1 半事務消息發送成功,進入 2。
1.2 半事務消息發送失敗,註冊系統不進行註冊,流程結束。(最終註冊系統與郵件通知系統數據一致
註冊系統開始註冊。
2.1 註冊成功,進入 3.1。
2.2 註冊失敗,進行 3.2。
註冊系統向消息隊列 RocketMQ 發送半消息狀態。
3.1 提交半事務消息,產生註冊成功消息,進入 4。
3.2 回滾半事務消息,未產生註冊成功消息,流程結束。(最終註冊系統與郵件通知系統數據一致)
郵件通知系統接收消息隊列 RocketMQ 的註冊成功消息。
郵件通知系統發送註冊成功郵件。(最終註冊系統與郵件通知系統數據一致)
總結:
分佈式事務針對的對象是data,它能夠在DB/緩存/ES/MQ中.
CAP模型中,CP、AP較多。前者強調一致性,後者強調吞吐量。而AP和CP這兩種模型,既有異步,又有同步的。
分佈式事務設計的核心要點是:解耦拆分。將分佈式事務拆成本地事務。
在分佈式系統設計中:同步主要用saga、異步主要使用事務消息。事務消息不須要寫補償接口,所以代碼侵入低。RocketMQ支持事務消息。