天天看點

解決分布式事務Seata

背景

既然要給大家講什麼是 seata,那就得先說一下這個東西的定位,這東西就是現在很火的 Spring Cloud Alibaba 裡的一個元件,是專門幫助我們解決分布式事務問題的,也就是說,seata 是一個分布式事務架構。

什麼是分布式事務

那可能很多小夥伴很蒙圈了,什麼是分布式事務?好吧,為了保證大家能繼續看下去,我們先說一下什麼是分布式事務這個問題。

舉個最簡單的例子,假設現在你負責了一個訂單系統,一個庫存系統,一個營銷系統,然後呢,當你的訂單系統收到使用者一個請求要建立訂單的時候,這個時候你得做三件事情。

第一,調用庫存系統的接口鎖定庫存,第二,調用調用營銷系統的接口鎖定優惠券,第三,你訂單系統自己得在 MySQL 裡插入一系列訂單的資料。

比如下圖 1 所示:

解決分布式事務Seata

那麼現在問題來了,你訂單系統有自己的訂單資料庫,可以去插入訂單資料,那庫存系統是不是也應該有自己的庫存資料庫,去鎖定庫存資料?

營銷系統是不是應該有自己的營銷資料庫,去鎖定優惠券?當然是了!每個人都有自己的資料庫,這一個都不能少。

如下圖 2 所示:

解決分布式事務Seata

那現在問題又來了,既然一次建立訂單的請求,要涉及到訂單、庫存、營銷三個系統,分别操作各自自己的三個資料庫,才能完成這次請求。

那是不是可能會出現這麼一種情況,首先呢,你先調用庫存系統,鎖定了庫存了,O 了。

接着呢,你又調用了營銷系統,鎖定了優惠券,也 O 了。最後呢,當你訂單系統要往自己的訂單資料庫裡插入資料的時候,網絡抽風了,導緻你這一次插入訂單資料失敗了,直接 exception 異常了,你蒙圈了。

如下圖 3 所示:

解決分布式事務Seata

那這個時候你覺得可能會産生什麼樣的問題呢,其實很簡單,這個時候你這個訂單要購買的商品庫存已經被鎖定了,你為了下這個訂單用的優惠券,也已經被鎖定了。

結果呢,你的訂單自己本身的資料并沒進入資料庫,然後還傳回一個了異常資訊給使用者說,本次下單失敗。

但是你說下單失敗就失敗吧,結果呢,營運看庫存資料的時候可能會一臉蒙圈,為啥有一些商品庫存被鎖定了,結果沒有對應的跟訂單,而且一直沒人付款來購買呢??

然後使用者自己也有點發蒙,因為一查自己的優惠券,好不容易攢了幾張券來買東西,結果現在訂單沒下成,優惠券狀态都搞成已使用了,自己還沒法用這些優惠券了。

如下圖 4 所示:

解決分布式事務Seata

其實這就是一個非常經典的分布式事務的問題了,你一個建立訂單的請求,橫跨了訂單、庫存、營銷三個系統,分别涉及三個資料庫。

所有很可能會發現,你的庫存和營銷的資料操作都成功了,而且庫存和營銷資料庫裡的本地事務都送出了,結果訂單插入資料庫失敗了,訂單資料庫裡的本地事務復原了,但是庫存和營銷資料庫裡的本地事務已經送出了,他們是不會復原的。

如下圖 5 所示:

解決分布式事務Seata

什麼叫做逆向補償

那既然問題已經找到了,我們希望的應該是什麼效果呢?

我們其實希望的效果是,如果訂單要是插入資料庫失敗了,訂單資料庫本地事務復原了,我們應該想辦法去通知一下庫存系統和營銷系統,把之前在庫存資料庫和營銷資料庫裡已經送出的資料修改做一個逆向補償,進行恢複。

什麼叫做逆向補償呢?意思就是說,之前庫存系統如果在資料庫裡執行的是 insert,那麼此時就應該執行 delete,把之前插入的資料删除了。

如果之前執行的 delete,現在就應該執行 insert,把删除的額資料重新插入回去,如果之前執行的是 udpate 語句,現在就應該再次執行一個 update 語句,把資料恢複到更新之前的狀态。

如下圖 6 所示:

解決分布式事務Seata

網際網路最流行的分布式事務元件 seata

那既然我們想要實作這個效果,這個時候問題就來了,單單依賴我們自己那肯定搞不定這個問題了,這個時候就必須引入 Spring Cloud Alibaba 裡的大佬元件,seata。

seata 就是專門幫助我們解決這個問題的,如果我們要是在系統裡引入 seata 架構之後,其實每個系統裡都會嵌入 seata,同時我們還需要去部署一個 seata server。

如下圖 7 所示:

解決分布式事務Seata

這個時候,我們的系統運作原理會變成這樣:訂單系統中的 seata 會發送請求給 seata server 去開啟一個全局事務,然後庫存系統先運作,他在進行資料庫 crud 的時候,這些操作都會被 seata 架構進行攔截。

然後 seata 架構會在一個本地事務裡,把你的 sql 語句和逆向補償日志,一起插入到你的庫存資料庫裡去,在庫存資料庫裡必須有一個 undo_log 表,存儲 seata 的逆向補償日志。

那這個逆向補償日志是什麼呢?簡單,如果你的 sql 是 insert,那逆向補償日志可以幫助你後續建構 delete 語句來删除,如果你的 sql 是 update,那逆向補償日志可以記錄你更新之前的舊資料,他可以幫助你後續把資料 update 到老版本的狀态。

如下圖 8 所示:

解決分布式事務Seata

你庫存系統的 sql 語句和他們的補償日志,是在一個本地事務裡一起送出的,一起成功或者一起失敗,是以但凡你的庫存系統更新成功了,就一定會有對應的補償日志也會在庫存 資料庫裡的,以備不時之需,營銷系統其實也是相同的運作原理。

那麼假設說庫存系統和營銷系統,按照這個思路都執行完畢了,到訂單系統了,他結果撂挑子了,插入訂單資料庫失敗。

當然,在插入的時候其實也會有對應的補償日志會一起送出,但是因為這個時候網絡問題,導緻插入訂單和插入補償日志一起失敗了。

是以此時訂單系統的 seata 就會上報 seata server 說,大哥,我這兒完犢子了,您要不通知庫存和營銷兩個兄弟,逆向補償一下吧。

如下圖 9 所示:

解決分布式事務Seata

接着 seata server 發現說,這分布式事務都失敗了,那趕緊的,他會通知庫存系統和營銷系統裡的 seata 架構小兄弟說,兄弟們,趕緊的,把之前插入你們資料庫裡的 undo_log 表裡的補償日志拿出來,建構一下逆向補償 sql。

之前是 insert 你就給我弄個 delete,之前是 delete 你就給我弄個 insert,之前是 update 你還是 update,逆向補償 sql 趕緊跑一把,把資料給我恢複了,前隊改後隊,跑步前進,hurry up 起來。

如下圖 10 所示:

解決分布式事務Seata

總結