redis實現高併發下的搶購/秒殺功能

搶購/秒殺是如今很常見的一個應用場景,那麼高併發競爭下如何解決超搶(或超賣庫存不足爲負數的問題)呢?

常規寫法:

查詢出對應商品的庫存,看是否大於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,沒有出現“超搶/超賣”。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章