分布式事務的解決方案

前言

先聲明定硝,本文不會介紹諸如ACID、2PC毫目、CAP等概念性的問題(要介紹也是Ctrl CV (:?? )蔬啡,想了解的同學可自行Google~ 本文只記錄筆者工作中遇到的事務問題诲侮,以及解決方案。

在工作中星爪,事務問題是比較常見的浆西,同時也是比較危險的,稍一不注意就會背P0事故顽腾。那我們在工作中要如何解決事務問題近零,保證業(yè)務安全運行呢?

試想一個業(yè)務場景:用戶在電商網(wǎng)站下單某個商品抄肖,這時需要進行兩個操作:

  1. 更改訂單狀態(tài)
  2. 將商品從購物車中移除

如果操作1成功了久信,操作2卻失敗。這時用戶看到商品仍在購物車中漓摩,以為沒有下單成功裙士,又再次點擊下單,造成重復訂單管毙。

如果操作1失敗了腿椎,操作2卻成功。這時顯示下單失敗夭咬,但是購物車中的商品被移除了啃炸,用戶對此也會產生疑惑。

綜上卓舵,操作1南用、2 只能同時成功,或者同時失敗掏湾。 否則就會出現(xiàn)各種想象不到的異常情況裹虫。

那要怎么保證呢?這時候就需要事務

1. 數(shù)據(jù)庫事務

這應該算是最簡單的事務問題了融击,因為常用的數(shù)據(jù)庫本身也支持事務操作筑公。

針對以上場景,可以編寫偽代碼:

// 開啟一個事務
tx := db.Begin()
// 1.更改訂單狀態(tài)
tx.updateOrderStatus(xxxx) 
// 2.將商品從購物車中移除
tx.removeItemFromShoppingCart(xxxx)
// 出現(xiàn)錯誤砚嘴,回滾操作 1十酣、2
if err != nil {
    tx.rollback()
    return
 }
 //操作1、2都運行成功际长,提交事務
 tx.commit()

通過偽代碼不難發(fā)現(xiàn),數(shù)據(jù)庫事務只適用于操作同一個DB兴泥,但現(xiàn)實的項目中工育,往往是以微服務劃分各自的職責,訂單服務和購物車服務甚至都歸屬于不同的團隊搓彻,更別說用同一個數(shù)據(jù)庫了如绸。

這時候就需要使用分布式事務

2. 事務消息

還是上文的場景嘱朽,雖然訂單服務和購物車服務歸屬于不同的服務,但是服務之間的協(xié)作可以通過消息隊列實現(xiàn)怔接。

簡單來說搪泳,就是當更新訂單狀態(tài)成功之后,發(fā)送一條消息給購物車服務扼脐,然后購物車服務執(zhí)行移除操作

image

這樣岸军,乍一看好像沒什么問題,但深入思考之后會有幾個疑問:

  1. 訂單狀態(tài)更新成功了瓦侮,但消息發(fā)送失敗艰赞,要如何處理?
  2. 消息發(fā)送成功了肚吏,要怎么保證購物車服務一定能消費到方妖?

概括成一句話:如何保證消息的生產端和消費端的事務性

2.1 事務消息 - 生產端

image

還是以用戶下單場景解析事務消息是如何發(fā)送的:

  1. producer發(fā)送訂單狀態(tài)已更新的消息
  2. 消息發(fā)送成功
  3. 執(zhí)行本地事務,這時真正更新訂單的狀態(tài)
  4. 本地事務執(zhí)行成功罚攀,則提交該事務消息党觅,失敗則回滾消息。

但是考慮到網(wǎng)絡的原因斋泄,在發(fā)送commit或rollback的消息丟失了杯瞻,broker接收不到信息,無法進行下一步操作是己。

對于這個問題很好解決又兵,如步驟5:在Producer端提供一個回查的接口,供Broker定期回查本地事務的狀態(tài)卒废。然后可以根據(jù)反查結果決定回滾還是提交事務沛厨。

2.2 事務消息 - 消費端

話說大部分文章在介紹事務消息時都只側重于生產端,對消費端一筆帶過甚至提都不提
image

但其實在實戰(zhàn)中摔认,如何實現(xiàn)高效逆皮、正確的消費端也是一大難題。

想問大家一個問題参袱,在消費端电谣,是先消費消息再提交commit?還是先提交commit 再消費消息呢抹蚀?

其實這兩種方式?jīng)]有對錯之分剿牺,只是在不同業(yè)務下的選擇。

我們就以這兩種方式來介紹如何實現(xiàn)消費端的“事務性”

1. 先消費消息再提交commit

err := handle(msg)
if err!=nil{
    return err
}
consumer.commit(msg)

這種消費方式本身也具備事務性了环壤,因為只有消息消費成功晒来,才提交偏移量,如果消費失敗郑现,Broker則會重新投遞湃崩。(多次投遞失敗荧降,則會發(fā)送死信隊列)

image

但這種方式也有兩個缺點:

  1. Broker可能會多次投遞,造成重復消費攒读,所以消費者要實現(xiàn)好冪等邏輯
  2. 先消費再提交commit意味著不能異步多線程消費朵诫,消費速度較慢

2. 先提交commit再消費

consumer.commit(msg)
go handle(msg)

這種實現(xiàn)方式可以運用多線程異步消費,較于方式1能極大提升消費速度薄扁。但是同時也帶來了隱患剪返。

因為Broker只投遞一次消息,所以處理失敗case只能由業(yè)務自己去重試泌辫。

image

通用的方案是設計重試隊列随夸,當業(yè)務邏輯處理失敗時,交由重試隊列去處理震放,當重試超過一定次數(shù)宾毒,則需要告警人為干預。

注:這種實現(xiàn)方式殿遂,不能保證最終一致性诈铛,在極端情況下仍會出現(xiàn)不一致的情況。

對于事務消息的消費端墨礁,兩種實現(xiàn)方式都各有利弊幢竹,要深入業(yè)務調研,從而做出最好的選擇恩静。

對于分布式事務的解決方案焕毫,上文介紹了事務消息,對于這種方案驶乾,能保證最終的結果是可靠的邑飒,過程也非常簡單易理解。但是整個過程完全沒有任何隔離性可言级乐。

對于訂單和購物車的場景疙咸,對隔離性要求不高,所以使用事務消息來解決該種場景是非常合適的风科。

但是對于另一個場景:用戶下單某個商品撒轮,對應兩個操作:

  1. 更改訂單狀態(tài)
  2. 扣減商品庫存

如果使用缺乏隔離性的事務消息來處理該場景,會帶來一個顯而易見的問題“超賣”贼穆。

因為兩個客戶完全有可能在短時間內都成功購買了同一件商品题山,而且他們各自購買的數(shù)量都不超過目前的庫存,但他們購買的數(shù)量之和卻超過了庫存故痊。

所以就需要使用隔離性更強的分布式事務方案 -- TCC 事務來處理臀蛛。

3. TCC

在具體實現(xiàn)上,TCC 較為煩瑣崖蜜,它是一種業(yè)務侵入式較強的事務方案浊仆。要求業(yè)務處理過程必須拆分為“預留業(yè)務資源”和“確認/釋放消費資源”兩個子過程。如同 TCC 的名字所示豫领,它分為(Try抡柿、Confirm、Cancel)三個階段等恐。

圖片
  • Try:嘗試執(zhí)行階段洲劣,完成所有業(yè)務可執(zhí)行性的檢查(保障一致性),并且預留好全部需用到的業(yè)務資源(保障隔離性)课蔬。
  • Confirm:確認執(zhí)行階段囱稽,不進行任何業(yè)務檢查,直接使用 Try 階段準備的資源來完成業(yè)務處理二跋。Confirm 階段可能會重復執(zhí)行战惊,因此本階段所執(zhí)行的操作需要具備冪等性。
  • Cancel:取消執(zhí)行階段扎即,釋放 Try 階段預留的業(yè)務資源吞获。Cancel 階段可能會重復執(zhí)行,也需要滿足冪等性谚鄙。

業(yè)務時序圖:

圖片
  1. 訂單服務發(fā)起事務請求各拷,庫存服務&積分服務預留業(yè)務資源(凍結庫存、預添加積分)

  2. Try階段全部成功闷营,完成業(yè)務操作(扣減庫存烤黍,為會員添加積分)

  3. Try階段有操作失敗或超時,取消業(yè)務操作(釋放庫存傻盟、取消添加積分)

總結

分布式事務有多種解決方案速蕊,同一種方案,根據(jù)業(yè)務的不同也有不同的實現(xiàn)方式莫杈。所以要深入業(yè)務互例,選擇一個最合適的方案。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末筝闹,一起剝皮案震驚了整個濱河市媳叨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌关顷,老刑警劉巖糊秆,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異议双,居然都是意外死亡痘番,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汞舱,“玉大人伍纫,你說我怎么就攤上這事“何撸” “怎么了莹规?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泌神。 經(jīng)常有香客問我良漱,道長,這世上最難降的妖魔是什么欢际? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任母市,我火速辦了婚禮,結果婚禮上损趋,老公的妹妹穿的比我還像新娘患久。我一直安慰自己,他們只是感情好舶沿,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布墙杯。 她就那樣靜靜地躺著,像睡著了一般括荡。 火紅的嫁衣襯著肌膚如雪高镐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天畸冲,我揣著相機與錄音嫉髓,去河邊找鬼。 笑死邑闲,一個胖子當著我的面吹牛算行,可吹牛的內容都是我干的。 我是一名探鬼主播苫耸,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼州邢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了褪子?” 一聲冷哼從身側響起量淌,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嫌褪,沒想到半個月后呀枢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡笼痛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年裙秋,在試婚紗的時候發(fā)現(xiàn)自己被綠了琅拌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡摘刑,死狀恐怖进宝,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情泣侮,我是刑警寧澤即彪,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站活尊,受9級特大地震影響,放射性物質發(fā)生泄漏漏益。R本人自食惡果不足惜蛹锰,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绰疤。 院中可真熱鬧铜犬,春花似錦、人聲如沸轻庆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽余爆。三九已至纷宇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛾方,已是汗流浹背像捶。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桩砰,地道東北人拓春。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像亚隅,于是被迫代替她去往敵國和親硼莽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內容