mysql 超賣 mysql 解決超賣問題的鎖分析

2021-10-17 16:48:24 字數 1320 閱讀 1645

解決超賣問題,常見的方式,利用redis 的原子性去遞減;利用佇列,隊列入隊計數。或者直接打到mysql 層。由mysql 保證不超賣,有幾個玩法。利用屬性不一樣,挺有意思,記錄下。

首先,mysql 隔離級別是rr,或者是序列,但是不可能用序列,太慢。

其次,為什麼會出現超賣問題?因為這個select and update 是個非原子操作,是兩步操作。

最後,怎麼解決這個問題? 就在原子性上做文章,比如redis 的 lua 封裝,將指令封裝成原子操作。或者mysql 的互斥鎖,再或者,乾脆允許超賣,業務層做二次檢查。

第一種,mysql 在select 的時候不加互斥鎖,這個時候的做法:

1:開啟事務

2:查詢庫存,並顯示的設定寫鎖(排他鎖):select * from table_name where …

3:生成訂單

4:去庫存,隱示的設定寫鎖(排他鎖):update goods set counts = counts – 1 where id = 1

5:commit,釋放鎖

這裡提下mysql 的rc 和rr 區別,mysql 是如何實現可重複讀的? rr隔離級別是在事務開始時刻,確切地說是第乙個讀操作建立read view的;rc隔離級別是在語句開始時刻建立read view的。乙個read view 可以理解成乙份快照(底層只是事務id 列表),所以,rc 隔離級別下,多次讀可能受其他事務commit 導致讀取資料不一致。

那麼 rr 級別下,select 和 update 的時候, 資料是乙個version的,但是上面做法的問題,就在於,幾個事務begin 後,都讀到資料為1,就開始 update ,就會導致資料最終為負數,這種方式,避不了為負數。

我們可以做個二次容錯,檢查update 後資料為負數後,直接回滾這次事務即可。

第二種方式是select 的時候就加行鎖,select for update ,直接鎖住這條記錄,不讓其他事務讀。所以對這個資料,都成序列,這個缺點就是會影響效能,但是不會出現超賣的情況了。

1:開啟事務

2:查詢庫存,並顯示的設定寫鎖(排他鎖):select * from table_name where … for update

3:生成訂單

4:去庫存,隱示的設定寫鎖(排他鎖):update goods set counts = counts – 1 where id = 1

5:commit,釋放鎖。

第三種,試著給select 加讀鎖,這種做法是不行,會出現死鎖,什麼情況會死鎖呢?事務1,2同時加讀鎖,事務1加寫鎖等事務2的寫鎖,事務2的加寫鎖等事務1的讀鎖,相互等待,陷入死鎖。那為啥 select for update 不會這樣,因為mysql 互斥鎖是可重入鎖啊。

mysql 樂觀鎖 超賣 秒殺超賣解決方案

方案一 redis事務處理 multi 我們可以使用redis中的監聽 watch 方法,去監聽庫存數量,一旦庫存數量在其他客戶端發生改變,後續操作則會失敗。watch key1 key2 監聽key1 key2有沒有變化,如果有變,則事務取消 方案二 redis分布式鎖 分布式鎖確保只有乙個執行緒...

什麼是超賣以及怎麼解決超賣

什麼是超賣 庫存只有1個,當有兩個執行緒過來後,都執行成功了,生成了兩個訂單,這就是超賣 避免超賣的發生 下面的 還是會發生超賣。雖然減庫存,生成訂單在同乙個事務,也對修改庫存做了限制,但是即使stock count變為0了,這個減庫存的sql操作也不會報錯,導致後面的生成訂單就會正常執行 導致庫存...

超賣現象及解決

本專案的超賣類似於電商的秒殺超賣現象 1.不同使用者在讀請求的時候,發現商品庫存足夠,然後同時發起請求,進行秒殺操作,減庫存,導致庫存減為負數。2.同乙個使用者在有庫存的時候,連續發出多個請求,兩個請求同時存在,於是生成多個訂單。對於第一種超賣現象 1 最簡單的方法,更新資料庫減庫存的時候,進行庫存...