搶購/秒殺是如今很常見的一個應用場景,那麼高併發競爭下如何解決超搶(或超賣庫存不足爲負數的問題)呢?
常規寫法:
查詢出對應商品的庫存,看是否大於0,然後執行生成訂單等操作,但是在判斷庫存是否大於0處,如果在高併發下就會有問題,導致庫存量出現負數
這裏我就只談redis的解決方案吧...
我們先來看以下代碼,是否能正確解決超搶/賣的問題:
比如這裏我先把庫存(可用庫存,這裏我強調下哈,一般都是商品詳情頁搶購,後來者進來看到的庫存可能不再是後臺系統配置的10個庫存數了)放入redis隊列:
$num=10; //庫存 $len=Redis::llen('goods_store:1'); //檢查庫存,goods_store:1 定義爲鍵名 $count = $num-$len; //實際庫存-被搶購的庫存 = 剩餘可用庫存 for($i=0;$i<$count;$i++) Redis::lpush('goods_store:1',1);//往goods_store列表中,未搶購之前這裏應該是默認滴push10個庫存數了 //echo \Redis::llen('goods_store:1');//未搶購之前這裏就是10了
好吧,搶購時間到了:
/* 模擬搶購操作,搶購前判斷redis隊列庫存量 */ $count=\Redis::lpop('goods_store:1');//lpop是移除並返回列表的第一個元素。 if(!$count) return '已經搶光了哦';
/* 下面處理搶購成功流程 */
DB('goods')->decrement('num', 1);//減少num庫存字段用戶搶購成功後,上面的我們也可以稍微優化下,比如我們可用將用戶ID存入了order:1列表中。接下來我們可以引導這些用戶去完成訂單的其他步驟,到這裏才涉及到與數據庫的交互。最終只有很少的人走到這一步吧,也就解決的數據庫的壓力問題。
我們再改下上面的代碼:
$user_id = Session::get('user_id');//當前搶購用戶id /* 模擬搶購操作,搶購前判斷redis隊列庫存量 */ $count=Redis::lpop('goods_store:1'); if(!$count) return '已經搶光了哦'; $result = Redis::lpush('order:1',$user_id); if($result) return '恭喜您!搶到了哦';爲了檢測實際效果,我使用jmeter工具模擬100、200、1000個用戶併發進行搶購,經過大量的測試,最終搶購成功的用戶始終爲10,沒有出現“超搶/超賣”。