由秒殺引起的一個問題
- 秒殺最大的一個問題就是解決超賣的問題。其中一種解決超賣以下方式:
1 update goods set num = num - 1 WHERE id = 1001 and num > 0
咱們假設如今商品只剩下一件了,此時數據庫中
num = 1;
但有100個線程同時讀取到了這個
num = 1,因此100個線程都開始減庫存了。
但你會最終會發覺,
其實只有一個線程減庫存成功,其餘99個線程所有失敗。
爲什麼?
這就是MySQL中的排他鎖起了做用。
排他鎖又稱爲寫鎖,簡稱X鎖,顧名思義,排他鎖就是不能與其餘所並存,
如一個事務獲取了一個數據行的排他鎖,其餘事務就不能再獲取該行的其餘鎖,包括共享鎖和排他鎖,可是獲取排他鎖的事務是能夠對數據就行讀取和修改。
就是相似於我在執行update操做的時候,這一行是一個事務
(默認加了排他鎖)。
這一行不能被任何其餘線程修改和讀寫
1 select version from goods WHERE id= 1001
2 update goods set num = num - 1, version = version + 1 WHERE id= 1001 AND num > 0 AND version = @version(上面查到的version);
這種方式採用了
版本號的方式,其實也就是
CAS的原理。
假設此時version = 100, num = 1; 100個線程進入到了這裏,同時他們select出來版本號都是version = 100。
而後直接update的時候,只有其中一個先update了,同時更新了版本號。
那麼其餘99個在更新的時候,會發覺version並不等於上次select的version,就說明version被其餘線程修改過了。那麼我就放棄此次update
利用redis的單線程預減庫存。好比商品有100件。那麼我在redis存儲一個k,v。例如 <gs1001, 100>
每個用戶線程進來,key值就減1,等減到0的時候,所有拒絕剩下的請求。
那麼也就是隻有100個線程會進入到後續操做。因此必定不會出現超賣的現象
可見第二種CAS是失敗重試,並沒有加鎖。應該比第一種加鎖效率要高不少。
相似於Java中的Synchronize和CAS。