庫存,訂單,積分的分布式事務

2022-03-09 20:47:28 字數 2722 閱讀 3327

乙個訂單支付之後,我們需要做下面的步驟:

減庫存可以採用同步呼叫(feign的方式),也可以採用非同步呼叫(rabbitmq傳遞訊息),我們這裡採用同步呼叫,接下來我們分析為什麼

如果我們採用非同步呼叫的方式,減庫存的這條訊息傳送到mq就不管了,那麼到底庫存減成功了沒有呢?這我們並不知道,如果庫存不足,那麼我們減庫存失敗,但是service的業務不會回滾,這個問題就是分布式事務問題,即跨服務的事務。減庫存這個業務從訂單微服務跨越到了商品微服務,而事務是由spring來管理的,兩套tomcat兩套spring,本身沒有任何關聯,但是卻是乙個事務,如果採用非同步,這邊的微服務執行失敗另一邊的微服務並不知道,破壞了事務的一致性,我們解決的方案是什麼呢?

變異步調用為同步呼叫,如果乙個微服務執行失敗就會丟擲異常,事務自然回滾(減庫存的操作只能放在建立訂單業務的最後,因為減庫存執行失敗事務自然回滾訂單也不會建立成功,但是如果上來就先減庫存,那玩意訂單建立失敗庫存無法回滾),但是這種方案也不是最優的,因為我們沒做優惠券功能,當我們做了優惠券功能,那計算優惠和減庫存哪個放在最後呢?哪個放在最後都不可行,這時候就必須解決分布式事務問題了

解決分布式事務問題:

2pc(兩階段提交)

第一階段,事務開始執行傳送一條訊息給相關的微服務告訴它們這個業務要開始執行,執行完畢後返回一條訊息,告訴這個微服務業務執行成功了沒有;

第二階,如果上一階段返回的訊息是執行成功,那麼再傳送一條訊息告訴所有微服務事務執行成功了,相關所有事務都可以提交了,如果第一階段有乙個微服務執行失敗,則所有事務都回滾

缺點:實現複雜、事務執行過程資料鎖定的範圍太大了,在本業務未執行完畢之前,資料庫相關的表都是鎖定狀態,因此這種處理方式效能較差,在高併發的業務中較少使用。

tcc(try-confirm-cancel):這種處理方式的前提是面對事務都要有一套確認事務執行的業務,一套取消執行的業務(即補償業務)。比如說減庫存這個業務,確認事務就是減庫存,補償事務就是加庫存。這種處理方式時所有業務都開始執行,互相不等待,完成了就提交,解決了兩階段提交問題中資料大面積鎖定的情況,但是如果業務a已經提交了,但是業務b失敗了,沒關係,會呼叫所有的補償事務,這種解決方案不是靠事務回滾的方式,靠的是事務的補償

缺點:解決了業務問題,但是使得業務變得複雜了,寫乙個業務必須寫乙個確定執行業務方法和乙個補償業務方法,除此之外還要考慮補償方案的失敗問題,當補償方案也執行失敗了呢,這時候就要考慮重試問題、人工介入問題

非同步確保:執行時傳送一條訊息,另一方接受訊息,如果執行不成功會一直重試,直到成功

缺點:事務無法回滾,不合適減庫存這個業務

2pc+mq:兩階段提交方式結合非同步確保

綜上,在電商行業中適用的還是tcc,雖然業務變得複雜了,但是行之有效;如果是轉賬業務,適合非同步確保,轉賬業務只需要訊息可靠就可以,執行時間晚一點也無妨,所以非同步確保的關鍵點是訊息的可靠

但是在我們這個小專案中,無需把業務變得這麼複雜,接下來討論我們採用的同步呼叫的解決方案。

同步呼叫中加鎖實現方式:

先查詢庫存,然後if判斷,庫存足夠就減庫存

邏輯是對的,但是這麼做有執行緒上的安全問題,當執行緒很多的時候,有可能引發超賣問題

加鎖:synchronized

效能太差了,只有乙個執行緒可以執行,當搭了集群時synchronized只鎖住了當前乙個tomcat,看起來是可行的,但是在分布式系統下是不安全的

分布式鎖:zookeeper

zookeeper是樹結構,它利用節點的唯一性來實現,加了分布式鎖以後,任何乙個邏輯進入到減庫存這個地方,都會建立乙個節點,建立成功就認為得到了鎖,繼續執行**;反之則失敗,返回或者wait,因此只有乙個人可以拿到這個鎖,執行完畢後刪除節點釋放鎖,其他人可以再次建立鎖

zookeeper可以建立臨時節點,當伺服器宕機或者斷開連線,會自動刪除節點,自動釋放鎖

缺點:實現起來太複雜

redis:setnx命令

原理類似於上述的 節點 ,只能set不存在的key,如果不存在則建立;如果存在它會set失敗,並返回0,拿到鎖以後可以使用del命令釋放鎖

缺點是存在搜尋問題,假如setnx成功,成功之後開始執行**,但是此時伺服器宕機,那del釋放鎖的命令一直沒有執行,相當於這個鎖一直被拿著,那麼這個值將無法再被set成功

但是這裡不推薦加鎖實現,因為用了鎖,就變成單執行緒了,相當於一執行這段**就把資料庫鎖死,同一時刻只能有乙個人來操作,這樣的實現類似於悲觀鎖,預設執行緒安全問題一定會發生,在面對高併發時,往往效能很差。

那既然不推薦悲觀鎖,是不是可以採用樂觀鎖呢?樂觀鎖是預設執行緒安全問題不會發生,不加鎖,但是不加鎖會有執行緒安全問題,那怎麼處理這件事情呢?

——我們不做查詢不做判斷,業務執行到減庫存**這裡之後直接開始減庫存,唉?這不是會超賣嗎?不要緊,我們的sql內部可以加條件來判斷,失敗則事務回滾,所有人不論怎麼操作,最後都會來運算元據庫,但是資料庫寫了判斷語句來判斷庫存,每個人來執行都會被判斷,本質上還是樂觀鎖。如果執行失敗會反饋失敗資訊,而不像是悲觀鎖那樣執行緒阻塞,導致一直等待,效能上來將,這種處理方式優於加鎖,我們的sql語句如下:

"update tb_stock set stock = stock - # where sku_id = # and stock >= #"

高併發場景庫存如何計算

分布式 分布式事務

是資料庫執行過程中的乙個邏輯單位,由乙個有限的資料庫操作序列構成。事務的acid四大特性 原子性 atomicity 事務作為乙個整體被執行。一致性 consistency 從乙個一致的狀態轉換到另乙個一致的狀態。隔離性 isolation 多個事務併發執行時,併發事務之間互相影響的程度。永續性 d...

分布式事務 分布式事務的實現

如果在多個服務中需要對不同的資料庫進行操作。因為不同服務操作的資料庫都不同,所以保證在同乙個事務中完成操作顯然是不科學的。那實現分布式事務的思想 1 方法入口,建立一條日誌記錄,狀態定義為初始狀態,即儲存本條日誌記錄 可以儲存在資料庫中,也可以寫出到本地磁碟檔案 2 可以在非同步執行緒或在定時任務中...

分布式之分布式事務

被人問到分布式事務,之前學rabbitmq 的時候學到過rabbitmq 高階的事務,因為沒有用過,所有沒有回答好。這裡總結一下。1.單機版事務。事務的四大特性 acid a.原子性 b.一致性 c.隔離性 d.永續性 單機事務可以通過設定事務的隔離級別 參見spring 的事務隔離級別 2.分布式...