閱讀目錄架構取經之路1 - 扣減庫存
架構取經之路2 - 熔斷機制
架構取經之路3 - 悟空聊無事務
回到頂部
悟空來到了這家**的後台,找到了開發人員「小黑熊」。
悟空:嘿,快查下我**的訂單,錢都給了,福袋怎麼還沒有到?
小黑熊:大聖,我們也收到異常通知了,更新福袋表的時候因網路原因導致福袋記錄沒有更新成功,所以福袋還是未傳送的。
悟空:福袋沒發出來,那為什麼訂單狀態還一直是已支付?你這小兒,可不要瞞我!
小黑熊:大聖,我們資料庫用的是mongodb 3.0,不支援事務啊。
悟空:你說的事務是什麼意思?
小黑熊:事務就是保持多個更新或刪除或增加操作,要麼都成功,要麼都失敗。
悟空:也就是說第一步頂單狀態從未支付到訂單成功已經執行成功了,但是第二步更新福袋的時候失敗了,沒有自動將第一步訂單的狀態給改回去?
小黑熊:是的,大聖。
悟空:那你們怎麼沒有退款啊?
小黑熊:大聖,我們也沒有想到有這種異常發生。
悟空:容我看下你們的**。
回到頂部
那這樣的**會有什麼問題呢?
如果第一步執行成功,第二步執行失敗了,丟擲了異常,則第一步訂單狀態還是訂單成功的,福袋狀態未更新,也就是**遇到的問題。
那如何保證兩步操作的一致性呢?(要麼都更新,要麼都不更新。)
我們都知道sql中是有事務這種解決方案的,我們先來看看sql中的事務。
回到頂部
之前寫過一篇文章,專門來講sql中的事務:30分鐘全面解析-sql事務+隔離級別+阻塞+死鎖。在這裡用偽**來說明下什麼事務。
舉個購買商品的例子:使用者下了一筆單,付款了,然後發放福袋,涉及到訂單表order更新,福袋表luckybag更新。
start transaction
// 開始事務
try
catch
(excption e)
end transaction
// 結束事務
更新訂單狀態和更新福袋狀態兩部操作成功,則全部提交到資料庫執行,如果其中任意一步出現問題,則全部回滾,就像沒有執行更新操作一樣,以保證資料的一致性。
回到頂部
由於mongodb 3.0 不支援事務,所以很有可能出現資料不一致的情況(訂單已支付,福袋未傳送)。
那我們既然不能享受到事務的一致性,有什麼辦法來優化這部分**呢?
我們先看下**的時序圖:
從上面的順序圖來看,分步儲存是有問題的,第一步儲存成功後,第二步如果儲存失敗,則資料不一致。那我們可以將儲存往後移嗎?
我們來看下優化後的時序圖,整體將儲存往後移。
偽**如下:
try
catch
(excption e)
那這種方式又有什麼優缺點呢?
優點:前四步的業務邏輯處理任意一步如果出錯了,並不會影響資料庫的記錄
缺點:後三步的儲存如果出錯了,和最開始的方案一樣,存在資料不一致的問題。
那如何進行解決這種問題?
回到頂部
優化後的**還是可能存在資料不一致的情況,那我們怎麼來解決?
問題1.如果福袋沒有自動發出去,現在還可以補發嗎?怎麼補發?
問題2.可以退款嗎?手動退款還是自動退款?分別有什麼優點和缺點?怎麼優化?
問題3.如果第三步更新庫存失敗,那又該怎麼做呢?
問題4.如何退款失敗,那又該怎麼做呢?
圍繞上面幾個問題,我們展開來論述。
方案2:將失敗的資料放到佇列裡面(可以是存到資料庫或者redis裡面,建議存放到資料庫),定時從佇列裡面獲取異常資料,進行重新傳送。
優點:(1)如果是臨時出現的網路問題,可以立即在短時間內重試幾次,可以解決問題。
缺點:(1)如果是介面或資料問題,短時間內重試再多次也是會失敗的;
(2)另外如果有大量失敗,重試也是會占用系統資源的。
優點:(1)將重試放到非同步任務中來做,可以減少系統資源的占用;
(2)如果是長時間出現的網路問題,等網路恢復後,一定會重試成功;
缺點:(1)異常資料無法通過重試來解決,則佇列裡面的資料將一直會進行重試,無法終止;
(2)如果有大量資料因介面或**問題導致失敗,則會積累大量失敗資料,而大量資料進行重試也會對系統資源造成一定壓力;
(3)重試失敗會進行error log的記錄,大量的error log對線上排查問題會造成干擾。
那補發如果一直失敗,是不是還有更好的方式?給使用者退款是不是更合理?(顧客等得很著急,趕緊把錢先退了吧。)這其實就是一種補償措施。
當然可以退款
優點:減少運營人員的工作量
缺點:在某些情況下,異常訂單需要多方排查核實才能退款,就不能走自動退款。比如**的邏輯沒有handle某些場景,一刀切的退款會導致錢退了,商品還發給了客戶。
那怎麼優化? 提供自動和手動的兩種方式,當某些異常場景需要手動退款的,等開發人員核實後,再進行手動退款。
賬不平怎麼處理?通過對賬的方式找出哪些賬不平。
我們很容易想到的方案是及時retry或 佇列retry。那有什麼問題呢?對於秒殺活動,佇列retry肯定不可行。
那我們可以做一次補償操作嗎?(發起退款,更新訂單狀態為失敗。)
答案是可以的。
每一步失敗我們都會做補償處理,但是中間某一步補償失敗,我們該怎麼處理?比如最後錢退不了。
常見方案:
1.退款失敗後主動報警通知運維人員或開發人員
2.手動退款(缺點:人工操作,容易出錯,比如找訂單找錯了)
在我現在做的專案都會將退款失敗的訊息以下面兩種形式推送給我:
2.雲服務商提供的日誌報警簡訊服務
這樣方便我去排查問題,以及快速退款。
回到頂部
我們可以設計乙個具有補償功能的解決方案:
1.如果第一步失敗,則發起退款
2.如果第二步失敗,則更新訂單狀態為失敗,並發起退款
3.如果第三步更新庫存失敗,則退回福袋,且更新訂單狀態為失敗,並發起退款
4.如果第四步更新訂單為成功時失敗,則庫存+1,退回福袋,更新訂單狀態失敗,並發起退款
回到頂部
作 者:jackson0714
出 處:
西天取經python之路 DAY(十二)
import module1,module2,module3 from module1 import from module1 import prt as print m1 注意 直接使用import module1的時候,呼叫module1裡的prt方法應該使用如下寫法 module1.prt 如...
西天取經python之路 DAY(十四)
目錄 range 模組 range模組常用方法 range模組例項 4位驗證碼例項 備註 encoding utf 8 author ccq file randomtest.py time 2019 8 3 15 37 import random print random.random 0 1之間的...
取經之路 長連線和短鏈結
本質上是tcp的長連線和短鏈結 http是乙個應用層協議 tcp是乙個傳輸層協議 ip是乙個網路層協議 tcp解決如何在ip層上可靠的傳輸資料 http是乙個無狀態的協議,對事務的處理沒有記憶能力 http 1.0預設使用短鏈結 http 1.1起預設使用長連線通過請求頭設定 connection ...