「要點解析」分布式高級商城業(yè)務:分布式事務器钟,滿足你的好奇心

訂單服務—分布式事務

本地事務

事務的基本性質

  • 數(shù)據(jù)庫事務的幾個特性:原子性(Atomicity)、一致性(Consistency)傲霸、隔離性或者獨立性(Lsolation)和持久性(Durabilily),簡稱就是ACID原子性:一系列的操作整體不可拆分昙啄,要么同時成功梳凛,要么同時失敗一致性:數(shù)據(jù)在事務的前后韧拒,業(yè)務整體一致轉賬:A:1000叛溢;B:1000楷掉;轉 200靖诗;事務成功:A:800查邢;B:1200隔離性:事務之間互相隔離持久性:一旦事務成功促绵,數(shù)據(jù)一定會落盤在數(shù)據(jù)庫
  • 在以往的單體應用中,我們多個業(yè)務操作使用同一條連接操作不同的數(shù)據(jù)庫表浓冒,一旦有異常稳懒,我們可以很容易地整體回滾

undo和redo

  • 其中原子性和持久性就要 靠undo和redo日志來實現(xiàn)
  • 在數(shù)據(jù)庫系統(tǒng)中场梆,既有存放數(shù)據(jù)的文件,也有存放日志的文件寞忿。日志在內存中也是有緩存Log buffer腔彰,也有磁盤文件log fileMySQL中的日志文件霹抛,有這么兩種與事務有關:undo日志與redo日志

undo日志

  • 數(shù)據(jù)庫事務具備原子性(Atomicity),如果事務執(zhí)行失敗雏搂,需要把數(shù)據(jù)回滾凸郑;事務同時還具備持久性(Durability)芙沥,事務對數(shù)據(jù)所做的變更就完全保存在了數(shù)據(jù)庫而昨,不能因為故障而丟失
  • 原子性可以利用undo日志來實現(xiàn)
  • undo日志的原理很簡單歌憨,為了滿足事務的原子性务嫡,在操作任何數(shù)據(jù)之前漆改,首先將數(shù)據(jù)備份到undo log挫剑;然后進行數(shù)據(jù)的修改樊破,如果出現(xiàn)了錯誤或者用戶執(zhí)行了rollback語句,系統(tǒng)可以利用undo log中的備份將數(shù)據(jù)恢復到事務開始之前的狀態(tài)數(shù)據(jù)庫寫入數(shù)據(jù)到磁盤之前或链,會把 數(shù)據(jù)先緩存在內存中 澳盐, 事務提交時才會寫入磁盤中用undo log實現(xiàn)原子性和持久化的事務簡單過程:假設有A叼耙、B兩個數(shù)據(jù)筛婉,值分別為1,21爽撒、事務開始2硕勿、記錄A=1到undo log3源武、修改A=34粱栖、記錄B=2到undo log5查排、修改B=46跋核、將undo log 寫到磁盤7砂代、將數(shù)據(jù)寫到磁盤8刻伊、事務提交如何保證持久性捶箱?事務提交前丁屎,會把修改數(shù)據(jù)提交到磁盤晨川,也就是說只要事務提交了共虑,數(shù)據(jù)肯定持久化了如何保證原子性妈拌?每次對數(shù)據(jù)庫修改尘分,都會把修改前數(shù)據(jù)記錄在undo log音诫,那么需要回滾時竭钝,可以讀取undo log,恢復數(shù)據(jù)若系統(tǒng)在7和8直接崩潰香罐,此時事務并未提交庇茫,需要回滾旦签,而undo log已經(jīng)被持久化宁炫,可以根據(jù)undo log來恢復數(shù)據(jù)若系統(tǒng)在7之前崩潰羔巢,此時數(shù)據(jù)并未持久化到磁盤竿秆,依然保持在事務之前的狀態(tài)
  • 缺陷:每個事務提交前將數(shù)據(jù)和undo log寫入磁盤幽钢,這樣會導致大量的磁盤IO搅吁,因此性能很低如果能夠將數(shù)據(jù)緩存一段時間肚豺,就能減少IO提高性能界拦,但是這樣就會喪失事務的持久性截碴,因此引入了另外一種機制來實現(xiàn)持久化日丹,即redo log

redo日志

  • 和undo log相反哲虾,redo log記錄的是 新數(shù)據(jù)的備份 束凑,在事務提交之前汪诉,只要redo log持久化即可扒寄,不需要將數(shù)據(jù)持久化(異步该编,數(shù)據(jù)可以在事務提交以后異步寫入磁盤)上渴,減少了IO的次數(shù)
  • Undo + Redo事務的簡化過程假設有A稠氮、B兩個數(shù)據(jù)赃份,值分別為1,21抓韩、事務開始2谒拴、記錄A=1到undo log buffer3英上、修改A=34苍日、記錄A=3到redo log buffer5相恃、記錄B=2到undo log buffer6拦耐、修改B=47幽邓、記錄B=4 到 redo log buffer8牵舵、將undo log 寫入redo log9畸颅、將redo log 寫入磁盤10没炒、事務提交
  • 安全和性能問題如何保證原子性涛癌?如果在事務提交前故障,通過undo log日志恢復數(shù)據(jù),如果undo log都還沒寫入拳话,那么數(shù)據(jù)就在尚未持久化先匪,無需回滾如何保證持久化大家會發(fā)現(xiàn),這里并沒有出現(xiàn)數(shù)據(jù)的持久化弃衍,因為數(shù)據(jù)已經(jīng)寫入redo log,而redo log持久化到了硬盤镜盯,因此只有到了步驟9以后岸裙,事務是可以提交的內存中的數(shù)據(jù)庫數(shù)據(jù)何時持久化到磁盤因為redo log已經(jīng)持久化,因此數(shù)據(jù)庫數(shù)據(jù)寫入磁盤與否影響不大速缆,不過為了避免出現(xiàn)臟數(shù)據(jù)(內存中與磁盤不一致)降允,事務提交后也會將內存數(shù)據(jù)刷入磁盤(也可以按照設定的頻率刷新內存數(shù)據(jù)到磁盤中)redo log何時寫入磁盤redo log會在事務提交之前,或者redo log buffer 滿了的時候寫入磁盤
  • 這里存在兩個問題:問題1:之前寫undo和數(shù)據(jù)庫數(shù)據(jù)寫到硬盤艺糜,現(xiàn)在是寫undo和redo到磁盤拟糕,似乎減少了IO次數(shù)數(shù)據(jù)庫數(shù)據(jù)寫入是隨機IO,性能很差redo log 在初始化時會開辟一段連續(xù)的空間倦踢,寫入是順序IO送滞,性能很好實際上undo log 并不是直接寫入磁盤,而是先寫入到redo log buffer中辱挥,當redo log持久化時犁嗅,undo log就同時持久化到硬盤了因此事務提交前,只需要對redo log持久化即可另外晤碘,redo log并不是寫入一次就持久化一次褂微,redo log在內存中也有自己的緩沖池;redo log buffer园爷,每次寫redo log都是寫入到buffer宠蚂,在提交時一次性持久化到磁盤,減少IO次數(shù)問題2:redo log個數(shù)據(jù)是寫入內存buffer中童社,當buffer滿或者事務提交時求厕,將buffer數(shù)據(jù)寫入磁盤;redo log中記錄的數(shù)據(jù)扰楼,有可能包含尚未提交事務呀癣,如果此時數(shù)據(jù)庫崩潰,那么如何完成數(shù)據(jù)恢復弦赖?數(shù)據(jù)恢復的兩種策略:恢復時项栏,只重做已經(jīng)提交了的事務恢復時,重做所有事務包括未提交的事務和回滾了的事務蹬竖,然后通過undo log回滾那些未提交的事務InnoDB引擎采用的第二種方案沼沈,因此undo log要在redo log前持久化

事務的隔離級別

  • READ UNCOMMITTED(讀未提交)該隔離級別的事務會讀到其它提交事務的數(shù)據(jù)流酬,此現(xiàn)象也稱之為臟讀
  • READ COMMITTED(讀提交)一個事務可以讀取另一個已提交的事務,多次讀取會造成不一樣的結果列另,此現(xiàn)象稱為不可重復讀的問題康吵,Oracle 和 SQLServer 的默認隔離級別
  • REPEATABLE READ(可重復讀)該隔離級別是MySQL默認的隔離級別,在同一個事務里访递,select的結果是事務開始時間點的狀態(tài)晦嵌,因此,同樣的select操作讀到的結果會是一致的拷姿,但是惭载,會有幻讀現(xiàn)象;MySQL 的 InnoDB 引擎可以通過 next-key locks 機制(參考下文"行鎖的算法"一節(jié))來避免幻讀
  • SERIALIZABLE(序列化)在該隔離級別下的事務都是串行順序執(zhí)行的响巢,MySQL 數(shù)據(jù)庫的 InnoDB引擎會給讀操作隱式加一把讀共享鎖描滔,從而避免了臟讀、不可重復讀和幻讀問題

事務的傳播行為

  • 1踪古、PROPAGATION_REQUIRED:如果當前沒有事務含长,就創(chuàng)建一個新事務,如果當前存在事務伏穆,就加入該事務拘泞,該設置是最常用的設置
  • 2、PROPAGATION_SUPPORTS:支持當前事務枕扫,如果當前存在事務陪腌,就加入該事務,如果當前不存在事務烟瞧,就以非事務執(zhí)行
  • 3诗鸭、PROPAGATION_MANDATORY:支持當前事務,如果當前存在事務参滴,就加入該事務强岸,如果當前不存在事務,就拋出異常
  • 4砾赔、PROPAGATION_REQUIRES_NEW:創(chuàng)建新事務蝌箍,無論當前存不存在事務,都創(chuàng)建新事務
  • 5过蹂、PROPAGATION_NOT_SUPPORTED:以非事務方式執(zhí)行操作十绑,如果當前存在事務聚至,就把當前事務掛起
  • 6酷勺、PROPAGATION_NEVER:以非事務方式執(zhí)行,如果當前存在事務扳躬,則拋出異常
  • 7脆诉、PROPAGATION_NESTED:如果當前存在事務甚亭,則在嵌套事務內執(zhí)行,如果當前沒有事務击胜,則執(zhí)行與PROPAGATION_REQUIRED類似的操作

本地事務失效問題

  • 同一個對象內事務方法互調默認失效亏狰,原因:繞過了代理對象;事務使用代理對象來控制的
  • 解決方案:使用同一個代理對象來調用事務方法引入 aop (aop-starter)偶摔;引入 aspectj@EnableAspectJAutoProxy 注解:開啟 aspectj 動態(tài)代理功能暇唾,以后所有的動態(tài)代理都是aspectj創(chuàng)建的,即使沒有接口辰斋,也可以創(chuàng)建動態(tài)代理@EnableAspectJAutoProxy(exposePeoxy=true) :對外暴露代理對象用代理對象本類互調: AopContext.currentProxy()

分布式事務

  • 分布式事務策州,就是指不是在單個服務或者單個數(shù)據(jù)庫架構下,產(chǎn)生的事務:跨數(shù)據(jù)源的分布式事務跨服務的分布式事務綜合情況

本地事務在分布式下的問題

「要點解析」分布式高級商城業(yè)務:分布式事務宫仗,滿足你的好奇心
  • 事務保證:1够挂、訂單服務異常,庫存鎖定不運行藕夫,全部回滾孽糖,撤銷操作2、庫存服務事務自治毅贮,鎖定失敗全部回滾訂單服務感受到办悟,繼續(xù)回滾3、庫存服務鎖定成功了滩褥,但是網(wǎng)絡原因返回數(shù)據(jù)途中出現(xiàn)問題誉尖?4、庫存服務鎖定成功了铸题,庫存服務下面的邏輯發(fā)生故障铡恕,訂單服務回滾,怎么處理丢间?
  • 問題:1探熔、遠程服務假失敗:遠程服務其實成功了烘挫,由于網(wǎng)絡故障等沒有返回诀艰;導致:訂單回滾、庫存卻扣減了2饮六、遠程服務執(zhí)行完成其垄,下面的其他方法出現(xiàn)問題;導致已執(zhí)行的遠程請求卤橄,肯定不能回滾本地事務绿满,在分布式系統(tǒng)下,只能控制住自己的回滾窟扑,控制不了其他服務的回滾分布式事務:最大原因 -- 網(wǎng)絡問題 + 分布式機器利用消息隊列實現(xiàn)最終一致性庫存服務鎖定成功后發(fā)送給消息隊列(當前庫存工作單)喇颁,過段時間自動解鎖漏健,解鎖時先查詢訂單的支付狀態(tài),解鎖成功修改庫存工作單詳情項狀態(tài)為已解鎖

為什么有分布式事務

  • 分布式系統(tǒng)經(jīng)常出現(xiàn)的異常機器宕機橘霎、網(wǎng)絡異常蔫浆、消息丟失、消息亂序姐叁、數(shù)據(jù)錯誤瓦盛、不可靠的TCP、存儲數(shù)據(jù)丟失...分布式事務是企業(yè)集成中的一個技術難點外潜,也是每一個分布式系統(tǒng)架構中都會涉及到的一個東西谭溉,特別是在微服務架構中,幾乎可以說是無法避免

CAP定理與BASE理論

CAP定理

  • CAP原則又叫CAP定理橡卤,值得是一個分布式系統(tǒng)中一致性(Consistency):在分布式系統(tǒng)中的所有數(shù)據(jù)備份扮念,在同一時刻是否同樣的值(等同于所有節(jié)點訪問同一份最新的數(shù)據(jù)副本)可用性(Availability):在集群中一部分節(jié)點故障后,集群整體是否還能響應客戶端的讀寫請求(對數(shù)據(jù)更新具備高可用性)分區(qū)容錯性(Partition tolerance):大多數(shù)分布式系統(tǒng)都分布在多個子網(wǎng)絡碧库,每個子網(wǎng)絡就叫做一個區(qū)(partition),分區(qū)容錯的意思是柜与,區(qū)間通信可能失敗嵌灰;比如:一臺服務器放在中國弄匕,另一臺服務器放在美國,這就叫兩個區(qū)沽瞭,它們之間可能無法通信
  • CAP原則指的是:這三個要素最多只能同時實現(xiàn)兩點迁匠, 不可能三者兼得一般來說,分區(qū)容錯無法避免驹溃,因此可以任務CAP的P總是成立城丧。CAP定理告訴我們,剩下的C和A無法同時做到
  • 分布式系統(tǒng)中實現(xiàn)一致性的raft算法raft算法解讀

面臨的問題

  • 對于多數(shù)大型互聯(lián)網(wǎng)應用的場景豌鹤,主機眾多亡哄、部署分散,而且現(xiàn)在的集群規(guī)模越來越大布疙,所以節(jié)點故障蚊惯、網(wǎng)絡故障是常態(tài),而且要保證服務可以性達到99.9999%(N個9)灵临,即保證P和A截型,舍棄C

BASE理論

  • 是對CAP理論的延伸,思想是即使無法做到強一致性(CAP的一致性就是強一致性)儒溉,但可以采用適當?shù)牟扇∪跻恢滦曰陆梗?最終一致性
  • BASE是指基本可用(Basically Available)基本可用是指分布式系統(tǒng)在出現(xiàn)故障的時候,允許損失部分可用性(例如響應時間、功能上的可用性)赶诊, 允許損失部分可用性笼平。需要注意的是园骆,基本可用絕不等價于系統(tǒng)不可用響應時間上的損失:正常情況下搜索引擎需要在0.5秒之內返回給用戶相應的查詢結果舔痪,但由于出現(xiàn)故障(比如系統(tǒng)部分機房發(fā)生斷電或者斷網(wǎng)故障),查詢結果的響應時間增加到了1~2秒功能上的損失:購物網(wǎng)站在購物高峰(如雙十一)時锌唾,為了保護系統(tǒng)的穩(wěn)定性锄码,部分消費者可能會被引導到一個降級頁面軟狀態(tài)(Soft State)軟狀態(tài)是指允許系統(tǒng)存在中間狀態(tài),而該中間狀態(tài)不會影響系統(tǒng)整體可用性晌涕。分布式存儲中一般一份數(shù)據(jù)會有多個副本滋捶,允許不同副本同步的延時就是軟狀態(tài)的體現(xiàn),MySQL replication 的異步復制也是一種體現(xiàn)最終一致性(Eventual Consistency)最終一致性是指系統(tǒng)中的所有數(shù)據(jù)副本 經(jīng)過一定時間后 余黎,最終能到達到一致的狀態(tài)重窟;弱一致性和強一致性相反,最終一致性是弱一致性的一種特殊情況

強一致性惧财、弱一致性巡扇、最終一致性

  • 從客戶端角度,多進程并發(fā)訪問時垮衷,更新過的數(shù)據(jù)在不同的進程如何獲取的不同策略厅翔,決定了不同的一致性。對于關系型數(shù)據(jù)庫搀突,要求更新過的數(shù)據(jù)被后續(xù)的訪問都能看到刀闷,這是 強一致性 。如果能容忍后續(xù)的部分或者全部訪問不到仰迁,則是 弱一致性 甸昏。如果經(jīng)過一段時間后要求能訪問到更新后的數(shù)據(jù),則是 最終一致性

分布式事務的幾種方案

2PC模式:兩階段提交

  • 數(shù)據(jù)庫支持的2PC【2 phase commit 二階提交 】徐许,又叫做 XA TransactionsMySQL 從5.5版本開始支持筒扒,SQL Server 2005 開始支持,Oracle 7開始支持绊寻,其中花墩,XA是一個兩階段提交協(xié)議,該協(xié)議分為以下兩個階段:第一階段:事務協(xié)調器要求每個涉及到事務的數(shù)據(jù)庫預提交(precommit)此操作澄步,并反映是否可用提交第二階段:事務協(xié)調器要求每個數(shù)據(jù)庫提交數(shù)據(jù)其中冰蘑,如果有任何一個數(shù)據(jù)庫否決此次提交,那么所有數(shù)據(jù)庫都會被要求回滾它們在此事務中的那部分消息XA協(xié)議比較簡單村缸,而且一旦商業(yè)數(shù)據(jù)庫實現(xiàn)了XA協(xié)議祠肥,使用分布式事務的成本也比較低XA 性能不理想 ,特別是在交易下單鏈路梯皿,往往并發(fā)量很高仇箱,XA無法滿足高并發(fā)場景XA 目前在商業(yè)數(shù)據(jù)庫支持的比較理想县恕, 在mysql數(shù)據(jù)庫中支持的不太理想 ,mysql的XA實現(xiàn)剂桥,沒有記錄prepare階段日志忠烛,主備切換會導致主庫與備庫數(shù)據(jù)不一致許多nosql也沒用支持XA,這讓XA的應用場景變得非常狹隘也有3PC权逗,引入了超時機制(無論協(xié)調者還是參與者美尸,在向對方發(fā)送請求后,若長時間未收到回應則做出相應處理)

柔性事務 - TCC事務補償型方案

  • 剛性事務:遵循ACID原則斟薇,強一致性
  • 柔性事務:遵循BASE理論师坎,最終一致性與剛性事務不同,柔性事務允許一定時間內堪滨,不同節(jié)點的數(shù)據(jù)不一致胯陋,但要求最終一致一階段 prepare 行為:調用自定義的prepare邏輯二階段 commit 行為:調用自定義的commit邏輯二階段 rollback 行為:調用自定義的rollback邏輯所謂 TCC 模式,是指支持把自定義的分支事務納入到全局事務的管理中

柔性事務 - 最大努力通知方案

  • 按規(guī)律進行通知袱箱, 不保證數(shù)據(jù)一定能通知成功遏乔,但會提供可查詢操作接口進行核對 。這種方案主要用在與第三方系統(tǒng)通訊時犯眠,比如:調用微信或支付寶支付后的支付結果通知按灶,這種方案也是結合 MQ 進行實現(xiàn);例如:通過 MQ 發(fā)送http請求筐咧,設置最大通知次數(shù)鸯旁,達到通知次數(shù)后即不再通知案例:銀行通知、商戶通知等(各大交易業(yè)務平臺間的商戶通知:多次通知量蕊、查詢校對铺罢、對賬文件),支付寶的支付成功異步回調

柔性事務 - 可靠消息+最終一致性方案(異步確保型)

  • 這種實現(xiàn)方式的思路:其實是源于ebay,其基本的設計思路是將遠程分布式事務拆分成一系列的本地事務

基本原理

一般分為事務的發(fā)起者A和事務的其它參與者B:

  • 事務發(fā)起者A執(zhí)行本地事務
  • 事務發(fā)起者A通過MQ將需要執(zhí)行的事務信息發(fā)送給事務參與者B
  • 事務參與者B接收消息后執(zhí)行本地事務
「要點解析」分布式高級商城業(yè)務:分布式事務残炮,滿足你的好奇心

這個過程有點像你去學校食堂吃飯:

  • 拿著錢去收銀處韭赘,點一份紅燒牛肉面,付錢
  • 收銀處給你發(fā)一個小票势就,還有一個號牌泉瞻,你別把票弄丟了!
  • 你憑小票和號牌一定能領到一份紅燒牛肉面苞冯,不管要多久袖牙,重試幾次

幾個注意事項:

  • 事務發(fā)起者A必須確保本地事務成功后,消息一定發(fā)送成功
  • MQ必須確保消息正確投遞和持久化保存
  • 事務參與者B必須確保消息最終一定能消費舅锄,如果失敗需要多次重試
  • 事務B執(zhí)行失敗鞭达,會重試,但不會導致事務A回滾

本地消息表

  • 為了避免消息發(fā)送失敗或者丟失,我們可以把消息持久化到數(shù)據(jù)庫中畴蹭,實現(xiàn)時有簡化版本和解耦合版本兩種方式
  • 1坦仍、簡化版本事務發(fā)起者:開啟本地事務執(zhí)行事務相關業(yè)務發(fā)送消息到MQ把消息持久化到數(shù)據(jù)庫,標記為已發(fā)送提交本地事務事務接收者:接收消息開啟本地事務處理事務相關業(yè)務修改數(shù)據(jù)庫消息狀態(tài)為已消費提交本地事務額外的定時任務定時掃描表中超時未消費消息叨襟,重新發(fā)送優(yōu)點:與TCC相比繁扎,實現(xiàn)方式較為簡單,開發(fā)成本低缺點:數(shù)據(jù)一致性完全依賴于消息服務芹啥,因此消息服務必須可靠的需要處理被動業(yè)務方的冪等問題被動業(yè)務失敗不會導致主動業(yè)務的回滾锻离,而是重試被動的業(yè)務事務業(yè)務與消息發(fā)送業(yè)務耦合 铺峭、業(yè)務數(shù)據(jù)與消息表要在一起
  • 2墓怀、獨立消息服務為了解決上述問題,我們引入一個獨立的消息服務卫键,來完成對消息的持久化傀履、發(fā)送、確認莉炉、失敗重試等一系列行為钓账,大概模型如下:一次消息發(fā)送的時序圖:優(yōu)點:解除了事務業(yè)務與消息相關業(yè)務的耦合缺點:實現(xiàn)起來比較復雜

RocketMQ事務消息

  • RocketMQ本身自帶了事務消息,可以保證消息的可靠性絮宁,原理其實就是自帶了本地消息表梆暮,與上面的思路類似

RabbitMQ的消息確認

  • RabbitMQ確保消息不丟失的思路比較奇特,并沒有使用傳統(tǒng)的本地表绍昂,而是利用了消息的確認機制ack生產(chǎn)者確認機制:確保消息從生產(chǎn)者到達MQ不會有問題消息生產(chǎn)者發(fā)送消息到RabbitMQ時啦粹,可以設置一個異步的監(jiān)聽器,監(jiān)聽來自MQ的ACKMQ接收到消息后窘游,會返回一個回執(zhí)給生產(chǎn)者:消息到達交換機后路由失敗唠椭,會返回失敗ACK消息路由成功,持久化失敗忍饰,會返回失敗ACK消息路由成功贪嫂,持久化成功。會返回成功ACK生產(chǎn)者提前編寫好不同回執(zhí)的處理方式失敗回執(zhí):等待一定時間后重新發(fā)送成功回執(zhí):記錄日志等行為消費者確認機制:確保消息能夠被消費者正確消費消費者需要在監(jiān)聽隊列的時候手動ACK確認RabbitMQ把消息投遞給消費者后艾蓝,會等待消費者ACK力崇,接收到ACK后才刪除消息。如果沒有接收到ACK消息會一直保留在服務端赢织,如果消費者斷開連接或者異常后亮靴,消息會投遞給其它消費者消費者處理完消息,提交事務后敌厘,手動ACK台猴,如果執(zhí)行過程中拋出異常,則不會ACK,業(yè)務處理失敗饱狂,等待下一條消息
  • 經(jīng)過上面的兩種確認機制曹步,可以確保從消息生產(chǎn)者到消費者的消息安全,再結合生產(chǎn)者和消費者兩端的本地事務休讳,即可保證一個分布式事務的最終一致性

消息事務優(yōu)缺點

  • 優(yōu)點:業(yè)務相對簡單讲婚,不需要編寫三個階段業(yè)務是多個本地事務的結合,因此資源鎖定周期短俊柔,性能好
  • 缺點:代碼侵入依賴于MQ的可靠性消息發(fā)起者可以回滾筹麸,但消息參與者無法引起事務回滾事務時效性差,取決于MQ消息發(fā)送是否及時雏婶,還有消息參與者的執(zhí)行情況

AT模式

  • Seata開源了AT模式物赶,AT模式是一種無侵入的分布式事務解決方案,可以看做是對TCC或者二階段提交模型的一種優(yōu)化留晚,解決了TCC模式中的代碼侵入酵紫、編碼復雜等問題
  • 在AT模式下,用戶只需關注自己的業(yè)務SQL错维,用戶的業(yè)務SQL作為一階段奖地,seata框架會自動生成事務的二階段提交和回滾操作
  • 詳細看Seata介紹

Seata

  • Seata 是一款開源的分布式事務解決方案,致力于提供高性能和簡單易用的分布式事務服務赋焕;Seata 將為用戶提供了AT参歹、TCC、SAGA和XA事務模式隆判,為用戶打造一站式的分布式解決方案Seata官方文檔地址

AT模式

  • 和TCC的執(zhí)行很像犬庇,都是分兩個階段一階段:執(zhí)行本地事務,并返回執(zhí)行結果二階段:根據(jù)一階段的結果蜜氨,判斷二階段做法:提交或回滾
  • 但AT模式底層做的事情可完全不同械筛,而且第二階段根本不需要我們編寫,全部由seata自己實現(xiàn)了飒炎,也就是說:我們寫的 代碼與本地事務時代碼一樣 埋哟,無需手動處理分布式事務
  • 那么,AT模式如何實現(xiàn)無代碼侵入郎汪,如何幫我們自動實現(xiàn)二階段代碼的呢赤赊?一階段:在一階段,Seata會攔截業(yè)務SQL煞赢,首先解析SQL語義先朦,找到業(yè)務SQL要更新的業(yè)務數(shù)據(jù)划煮,在業(yè)務數(shù)據(jù)被更新前钱贯,將其保存成 before image厌处,然后執(zhí)行業(yè)務SQL更新業(yè)務數(shù)據(jù),在業(yè)務數(shù)據(jù)更新之后波俄,再將其保存成 after image,最后獲取全局行鎖懦铺, 提交事務 ,以上操作全部在一個數(shù)據(jù)庫事務內完成趁窃,這樣保證了一階段操作的原子性這里的 before image 和 after image 類似于數(shù)據(jù)庫的undo和redo日志,但其實是用數(shù)據(jù)庫模擬的二階段提交:二階段是提交的話急前,因為業(yè)務SQL在一階段已經(jīng)提交至數(shù)據(jù)庫醒陆,所以seata框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可二階段回滾:二階段如果是回滾的話叔汁,seata就需要回滾一階段已經(jīng)執(zhí)行的業(yè)務SQL统求,還原業(yè)務數(shù)據(jù)检碗,回滾方式便是用before image還原業(yè)務數(shù)據(jù)据块;但在還原前首先要校驗臟寫,對比數(shù)據(jù)庫當前業(yè)務數(shù)據(jù)和 after image折剃,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫另假。可以還原業(yè)務數(shù)據(jù)怕犁,如果不一致就說明有臟寫边篮,出現(xiàn)臟寫就需要轉換人工處理不過因為有全局鎖機制,所以可以降低出現(xiàn)臟寫的概率AT模式的一階段奏甫、二階段提交和回滾均由seata框架自動生成戈轿,用戶只需要編寫業(yè)務SQL,便能輕松接入分布式事務阵子,AT模式是一種對業(yè)務無任何侵入的分布式事務解決方案

Seata的分布式交易解決方案

  • 整體機制一階段:業(yè)務數(shù)據(jù)和回滾日志記錄在同一個本地事務中提交思杯,釋放本地鎖和連接資源二階段:提交異步化,非衬咏快速的完成回滾通過一階段的回滾日志進行反向補償
  • 示例:TC:事務協(xié)調者維護全局和分支事務狀態(tài)色乾,驅動全局事務提交或回滾TM:事務管理器定義全局事務的范圍,開始全局事務领突、提交或回滾全局事務RM:資源管理器管理分支事務處理的資源暖璧,與TC交談以注冊分支事務和報告分支事務的狀態(tài),并驅動分支事務提交或回滾

Seata 整合(*

  • 1君旦、每一個微服務 必須先創(chuàng)建 undo_log 表澎办,記錄回滾日志-- 注意此處0.3.0+ 增加唯一索引 ux_undo_log CREATE TABLE undo_log ( id bigint(20) NOT NULLAUTO_INCREMENT, branch_id bigint(20) NOT NULL, xid varchar(100) NOTNULL, context varchar(128) NOT NULL, rollback_info longblob NOT NULL,log_status int(11) NOT NULL, log_created datetime NOT NULL,log_modified datetime NOT NULL, ext varchar(100) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid,branch_id) )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 復制代碼
  • 2嘲碱、安裝事務協(xié)調器:seata - serverseata 1.2下載地址
  • 3悍汛、整合3.1离咐、導入依賴 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.2.0</version> </dependency> 復制代碼3.2、解壓并啟動 Seata - Serverregistry.conf :注冊中心配置指明使用nacos作為配置中心术陶,并指定nacos地址指明seata配置所在位置file.conf :指定事務日志存儲在哪里啟動Seata服務梧宫,啟動成功并注冊進nacos服務中心3.3、使用注解@GlobalTransactional運行示例3.4忌卤、修改想要加入全局事務的微服務的 yml 文件(order訂單服務和ware庫存服務)seata: #是否開啟spring-boot 自動裝配enabled: true #自定義事務組名稱 tx-service-group: order-mall-group #是否開啟數(shù)據(jù)源自動代理 enable-auto-data-source-proxy: true service: vgroupMapping: order-mall-group: default grouplist: #服務器上seata的地址 default: localhost:8091 enable-degrade: false application-id: ${spring.application.name} 復制代碼引入file.conf文件關于將配置參數(shù)改為yml后的問題service { disableGlobalTransaction = false } 復制代碼3.5驰徊、項目啟動訂單服務注冊成功庫存服務注冊成功seata-server 日志

分布式事務效果演示(AT模式)

未開啟分布式全局事務

  • 模擬在生成訂單、扣減庫存之后的積分扣減過程中發(fā)生異常牺弹,本地事務的情況下訂單服務會回滾例驹,但扣減庫存并沒有回滾兩個商品庫存鎖定 ,目前都是0瞧预,訂單數(shù)據(jù)也清空了結果:訂單成功回滾,但庫存依舊被鎖定 10和5

開啟分布式全局事務

  • 積分扣減過程中發(fā)生異常圆丹,訂單和扣減庫存都會回滾兩個商品庫存鎖定為本地事務未全部回滾導致的 10和5結果:全部回滾,庫存鎖定量依舊是10和5
  • 開啟全局事務

可靠消息模式(最終一致性)演示(*

  • AT模式(強一致性倦微,效率較慢)欣福,適用于非高并發(fā)場景,訂單下單服務屬于高并發(fā)嘉裤,因此選用 可靠消息服務+最終一致性的方案
  • 可靠消息模式下的下單流程

RabbitMQ延時隊列(實現(xiàn)定時任務)

  • 場景:未付款訂單价脾,超時一定時間后,系統(tǒng)自動取消訂單并釋放占用物品
  • 常用解決方案:spring的schedule定時任務輪詢數(shù)據(jù)庫缺點:消耗系統(tǒng)內存秋柄、增加了數(shù)據(jù)庫的壓力骇笔、存在較大的時間誤差解決:rabbitmq的消息TTL和死信Exchange結合

消息的TTL(Time To Live)

  • 消息的TTL就是消息的 存活時間
  • RabbitMQ可以對 隊列消息 分別設置TTL對隊列設置就是隊列沒有消費者連著的保留時間笨触, 也可以對每一個單獨的消息做單獨的設置芦劣。超過了這個時間寸认,我認為這個消息就死了串慰,稱之為死信如果隊列設置了灸叼,消息也設置了怜姿,那么會 取小的 沧卢。所以一個消息如果被路由到不同的隊列中,這個消息死亡的時間有可能不一樣(不同的隊列設置)立磁,這個消息死亡的時間有可能不一樣(不同的隊列設置)唱歧,這里單講單個消息的TTL,因為它才是實現(xiàn)延遲任務的關鍵颅崩,可以通過 設置消息的expiration字段或者x-message-ttl屬性來設置時間 沿后,兩者一樣的效果

Dead Letter Exchanges(DLX)

  • 一個消息在滿足如下條件下,會進 死信路由 漆弄,記住這里是路由而不是隊列撼唾,一個路由可以對應很多隊列(什么是死信)(basic.reject/basic.ack)requeue=false
  • Dead Letter Exchange其實就是一種普通的exchange,和創(chuàng)建其他exchange沒有兩樣券坞。只是在某一個設置Dead Letter Exchange的隊列中有消息過期了恨锚,會自動觸發(fā)消息的轉發(fā)课舍,發(fā)送到Dead Letter Exchange中去
  • 我們既可以控制消息在一段時間后變成死信筝尾,又可以控制變成死信的消息被路由到某一個指定的交換機筹淫, 結合二者损姜,就可以實現(xiàn)一個延時隊列

延時隊列實現(xiàn)(*

  • 第一種實現(xiàn)方式:給隊列設置過期時間
  • 第二種實現(xiàn)方式:給消息設置過期時間

演示場景

[圖片上傳失敗...(image-b92ae2-1614950423879)]

  • 設計規(guī)范建議(基于事件模型的交換機設計)1、交換機命名:業(yè)務+exchange棒卷;交換機為Topic2比规、路由鍵:事件.需要感知的業(yè)務(可以不寫)3、隊列名稱:事件+想要監(jiān)聽的服務名+queue4、綁定關系:事件.感知的業(yè)務創(chuàng)建所需的隊列做裙、交換機以及綁定關系第一種方式:AmqpAdmin(消息隊列章節(jié)有詳細使用方式)第二種方式:spring允許直接使用 @Bean 的方式聲明 Binding、Queue关串、Exchange注意:自動創(chuàng)建隊列吧碾、交換機 是在第一次連上mq户敬, 并開啟監(jiān)聽的時候
  • 模擬下單成功尿庐、訂單超時未支付抄瑟、消息進入死信、取消訂單

庫存解鎖

  • 第一步:創(chuàng)建所需的交換機和隊列
  • 第二步:庫存解鎖的場景1钞翔、下訂單成功,訂單過期沒有支付被系統(tǒng)自動取消席舍、被用戶手動取消2布轿、下訂單成功,庫存鎖定成功来颤,接下來的業(yè)務調用失敗汰扭,導致訂單回滾,之前鎖定的庫存就需要解鎖福铅;之前使用的 seata的AT模式萝毛,屬于強一致性,效率較慢 滑黔,因此使用 可靠消息模式 ,庫存自動解鎖庫存鎖定流程

定時關單

  • 通過延時隊列
  • 存在的問題:由于網(wǎng)絡原因,或者機器故障、卡頓,訂單一分鐘后開始關單椰苟,但這個過程耗時超過了一分鐘洁仗,這時到達兩分鐘的節(jié)點,庫存開始解鎖。導致卡頓的訂單永遠無法解鎖庫存
  • 解決:不僅僅依靠延時隊列谬运,關單成功追加主動通知解鎖/** * 訂單釋放直接和庫存釋放進行綁定 * @return / @Bean public Binding orderReleaseOtherBinding(){return* new Binding("stock.release.stock.queue", Binding.DestinationType.QUEUE, "order-event-exchange","order.release.other.#", null); } //關單成功滑废,主動通知解鎖庫存rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderEntity); 復制代碼
  • 可靠消息模式最終效果

如何保證消息可靠

消息丟失

  • 消息發(fā)送出去豁延,由于網(wǎng)絡問題沒有抵達服務器做好容錯方法(try-catch)袋狞,發(fā)送消息可能會網(wǎng)絡失敗,失敗后要有重試機制么库,可記錄數(shù)據(jù)庫,采用定期掃描重發(fā)的方式做好日志記錄注竿,每個消息狀態(tài)是否都被服務器收到都應該記錄做好定期重發(fā),如果消息沒有發(fā)送成功,定期去數(shù)據(jù)庫掃描未成功的消息進行重發(fā)
  • 消息抵達Broker,Broker要將消息寫入磁盤(持久化)才算成功,此時Broker尚未持久化完成入挣,宕機
  • 自動ACK的狀態(tài)下恢氯,消費者收到消息赶站,但沒來得及消費然后宕機一定要開啟手動ACK焙格,消費成功才移除,失敗或者沒來得及處理就NoAck并重新入隊

消息重復

  • 消息消費成功冬阳,事務已經(jīng)提交氯窍,ack時能犯,機器宕機传藏。導致沒有ack成功侈离,Broker的消費重新由unack變?yōu)閞eady,并發(fā)送給其他消費者
  • 消息消費失敗,由于重試機制筝蚕,自動又將消息發(fā)送出去
  • 成功消費卦碾,ack宕機時,消息由unack變?yōu)閞eady,Broker又重新發(fā)送消費者的業(yè)務消費接口應該設計為冪等性的起宽,比如扣庫存有工作單的狀態(tài)標識使用 防重表(redis/mysql),發(fā)送消息每一個都有業(yè)務的唯一標識洲胖,處理過的就不用處理rabbitMQ的每一個消息都有readLivered字段,可以獲取是否是被重新投遞過來的燎含,而不是第一次投遞過來的

消息積壓

  • 消費者宕機積壓
  • 消費者消費能力不足積壓
  • 發(fā)送者發(fā)送流量太大上線更多的消費者宾濒,進行正常消費上線專門的隊列消費服務,將消息先批量取出來屏箍,記錄數(shù)據(jù)庫绘梦,離線慢慢處理

以上就是有關分布式的學習筆記,希望可以對大家學習分布式有幫助赴魁,喜歡的小伙伴可以幫忙轉發(fā)+關注卸奉,感謝大家!后期也會不定時的更新干貨的颖御!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末榄棵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子潘拱,更是在濱河造成了極大的恐慌疹鳄,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芦岂,死亡現(xiàn)場離奇詭異瘪弓,居然都是意外死亡,警方通過查閱死者的電腦和手機禽最,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門腺怯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袱饭,“玉大人,你說我怎么就攤上這事呛占÷枪裕” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵晾虑,是天一觀的道長疹味。 經(jīng)常有香客問我,道長走贪,這世上最難降的妖魔是什么佛猛? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮坠狡,結果婚禮上,老公的妹妹穿的比我還像新娘遂跟。我一直安慰自己逃沿,他們只是感情好,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布幻锁。 她就那樣靜靜地躺著凯亮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哄尔。 梳的紋絲不亂的頭發(fā)上假消,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音岭接,去河邊找鬼富拗。 笑死,一個胖子當著我的面吹牛鸣戴,可吹牛的內容都是我干的啃沪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼窄锅,長吁一口氣:“原來是場噩夢啊……” “哼创千!你這毒婦竟也來了?” 一聲冷哼從身側響起入偷,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤追驴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后疏之,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殿雪,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年体捏,在試婚紗的時候發(fā)現(xiàn)自己被綠了冠摄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糯崎。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖河泳,靈堂內的尸體忽然破棺而出沃呢,到底是詐尸還是另有隱情,我是刑警寧澤拆挥,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布薄霜,位于F島的核電站,受9級特大地震影響纸兔,放射性物質發(fā)生泄漏惰瓜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一汉矿、第九天 我趴在偏房一處隱蔽的房頂上張望崎坊。 院中可真熱鬧,春花似錦洲拇、人聲如沸奈揍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽男翰。三九已至,卻和暖如春纽乱,著一層夾襖步出監(jiān)牢的瞬間蛾绎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工鸦列, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留租冠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓敛熬,卻偏偏與公主長得像肺稀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子应民,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內容