答面試官問:如何防超賣,有幾種實現方式

Martist發表於2020-09-04

場景

第一種方法 悲觀鎖

悲觀併發控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)是一種併發控制的方法。它可以阻止一個事務以影響其他使用者的方式來修改資料。如果一個事務執行的操作讀某行資料應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖衝突的操作。

悲觀併發控制主要用於資料爭用激烈的環境,以及發生併發衝突時使用鎖保護資料的成本要低於回滾事務的成本的環境中。

簡而言之,悲觀鎖主要用於保護資料的完整性。當多個事務併發執行時,某個事務對資料應用了鎖,則其他事務只能等該事務執行完了,才能進行對該資料進行修改操作。


update goods set num = num - 1 WHERE id = 1001 and num > 0

假設現在商品只剩下一件了,此時資料庫中 num = 1;

但有100個執行緒同時讀取到了這個 num = 1,所以100個執行緒都開始減庫存了。

但你會最終會發覺,其實只有一個執行緒減庫存成功,其他99個執行緒全部失敗。

需要注意的是,FOR UPDATE生效需要同時滿足兩個條件時才生效:

  • 資料庫的引擎為 innoDB

  • 操作位於事務塊中(BEGIN/COMMIT)

悲觀鎖採用的是「先獲取鎖再訪問」的策略,來保障資料的安全。但是加鎖策略,依賴資料庫實現,會增加資料庫的負擔,且會增加死鎖的發生機率。此外,對於不會發生變化的只讀資料,加鎖只會增加額外不必要的負擔。在實際的實踐中,對於併發很高的場景並不會使用悲觀鎖,因為當一個事務鎖住了資料,那麼其他事務都會發生阻塞,會導致大量的事務發生積壓拖垮整個系統。

第二種辦法 樂觀鎖


select version from goods WHERE id= 1001

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訊息佇列

在秒殺的情況下,高頻率的去讀寫資料庫,會嚴重造成效能問題。所以必須藉助其他服務, 利用redis的單執行緒預減庫存。比如商品有100件。那麼我在redis儲存一個k,v。例如

每一個使用者執行緒進來,key值就減1,等減到0的時候,全部拒絕剩下的請求。

那麼也就是隻有100個執行緒會進入到後續操作。所以一定不會出現超賣的現象。

第四種辦法 redis分散式鎖


$expire = 10;//有效期10秒

$key = 'lock';//key

$value = time() + $expire;//鎖的值 = Unix時間戳 + 鎖的有效期

$lock = $redis->setnx($key, $value);

//判斷是否上鎖成功,成功則執行下步操作

if(!empty($lock))

{

//下單邏輯...

}

第四種方法,可以參考本書的這個章節:Redis實現分散式鎖

轉自我的電子書

www.kancloud.cn/martist/be_new_fri...

本作品採用《CC 協議》,轉載必須註明作者和本文連結
是非之外有一座花園,我們會在那裡相遇

相關文章