秒殺系統之超買問題詳解

1.場景

假設現在庫存只有一個商品了,多線程下如何保證最後庫存是0而不是負數

2. 方法

  • MySQL中的排他鎖
update goods set num = num - 1 WHERE id = 1001 and num > 0

排他鎖又稱爲寫鎖,簡稱X鎖,顧名思義,排他鎖就是不能與其他所並存,如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對數據就行讀取和修改。
就是類似於我在執行update操作的時候,這一行是一個事務(默認加了排他鎖)。這一行不能被任何其他線程修改和讀寫

  • 採用了版本號的方式
select version from goods WHERE id= 1001;
update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND version = @version(上面查到的version);
  1. 這種方式採用了版本號的方式,其實也就是CAS的原理
  2. 假設此時version = 100, num = 1;100個線程進入到了這裏,同時他們select出來版本號都是version = 100。 然後直接update的時候,只有其中一個先update了,同時更新了版本號。那麼其他99個在更新的時候,會發覺version並不等於上次select的version,就說明version被其他線程修改過了。那麼我就放棄這次update
  • 利用redis

利用redis的單線程預減庫存。比如商品有100件。那麼我在redis存儲一個k,v。例如 <gs1001, 100>
每一個用戶線程進來,key值就減1,等減到0的時候,全部拒絕剩下的請求。
那麼也就是隻有100個線程會進入到後續操作。所以一定不會出現超賣的現象

3.總結

  1. 第二種方式而且還應該在執行該sql語句前增加一個num數目是否大於0的業務邏輯判斷,而且該方式還是會加排它鎖的。
  2. 實際上,第一種方法和第二種兩種方式解決超賣的方式也有細微的一點區別
  1. 考慮兩個線程,當庫存數量爲2時。
  2. 如果是第一種方式,那麼兩個線程都能成功執行。
  3. 如果爲第二種方式,如果在第一個線程提交事務之前,第二個線程也執行了相同的sql拿到了version值(也就是線程1和線程2拿到了相同的version值),那麼這兩個線程之間將只有一個線程能夠讓庫存數目減一成功執行。最終庫存數目不爲0,而爲1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章