首頁 > 資料庫 > Redis > 怎麼用redis做秒殺支撐的demo

怎麼用redis做秒殺支撐的demo

WBOY
發布: 2023-05-27 08:22:48
轉載
1514 人瀏覽過

用redis做秒殺的庫存扣除, 限制每個帳號只能搶購一次, 這個簡單的demo使用了string, hash, list三種基本類型.

  • 用string類型的int值來儲存剩餘庫存, 並在搶購成功後減1

  • 用hash來儲存"已搶購到"的會員的id(可以確保用戶id作為field的唯一性). 注意: 這個hash的field對應的uid不一定搶購成功

  • 用list來保存真正搶購成功的會員id的列表, 作為後續處理訂單的隊列

第一次寫的時候, 嘗試過使用string的bitmap來保存該會員是否搶購成功過, 但是這個在高並發時會出問題, 所以後來換成了唯一field的hash

2個檔案:

  • init.php: 初始化庫存, 統計資料, 搶購成功的會員清單等

  • #buy.php: 搶購

初始化

ini.php:

$m_redis = new YourRedisClass(); //redis类很多, 可以自己写, 也可以用predis等
$m_redis->set('rush_stock', 20);//int, 可抢购的商品总数
$m_redis->set('rush_success', 0); //int, 成功的数量
$m_redis->set('rush_fail', 0); //int, 失败的数量
$m_redis->expire('rush_queue_h', 0); //hash, 已加入抢购队列的会员的hash记录表(field是唯一的, 可限制每个uid只有一次), 不一定抢购成功
$m_redis->set('rush_got_uid', ''); //string, 抢购成功的会员uid记录, 只是为了能简单的显示抢到的会员.
$m_redis->del('rush_got_uid_l'); //list, 抢购成功的会员uid(方便抢购后的订单批次处理)
echo 'success, '.date('Y-m-d H:i:s');
登入後複製

執行本檔案, 初始化數量.

redis-cli 下執行"mget rush_stock rush_fail rush_success rush_got_uid" 確認初始化資料

秒殺

判斷的邏輯:


  • ## 判斷的邏輯:
    1. ## 判斷的邏輯:

    #庫存是否為0, 庫存>0則進入搶購佇列


    1. 搶購佇列資料(hash)寫入成功, 則準備扣繳庫存

##庫存扣減成功(餘數>=0)則搶購成功, 進入訂單處理隊列(list)

目前是用string int存儲庫存, 也可以用list的item的個數來計數, 但是初始化時沒有string類型來得簡單.

#buy.php###
//随机生成会员id
$uid = rand(1,200);

$m_redis = new YourRedisClass(); //redis类很多, 可以自己写, 也可以用predis等

$key = 'rush_stock';
$q = $m_redis->get($key);

//1. 先判断库存数量
//库存为0, 直接无法进入抢购队列
if($q < 1){
    $m_redis->incr(&#39;rush_fail&#39;);//记录失败的数量
    die($uid.&#39;:OutOfStock&#39;);
}

//2. 判断该会员是否购买过 => 是否进入过队列
$queued = $m_redis->hSet(&#39;rush_queue_h&#39;, $uid, $uid);//这里只能判断是否进入了抢购的队列. 如果库存为0则无法进入. 进入了队列后才能抢购
if(!$queued){
    $m_redis->incr(&#39;rush_fail&#39;);//记录失败的数量
    die($uid.&#39;:queue failed&#39;);
}

//让cpu飞一会
$n = rand(20000,100000);
for($i=0; $i < $n; $i++){
    $a = rand(1,20000);
    $a = rand(1,30000);
    $a = rand(1,40000);
    $a = rand(1,50000);
    $a = rand(1,60000);
    $a = rand(1,70000);
    $a = rand(1,80000);
    $a = rand(1,90000);
}


//3. 扣减数量
$q = $m_redis->decr($key, 1);//扣减数量后会返回结果值
echo $q.&#39; left:&#39;;


////region 如果不判断操作后返回的结果,则可能会造成超发
//$m_redis->incr(&#39;q_success&#39;);//记录成功的数量  ==>这个是有bug的, 不可取
//die(&#39;:success&#39;);
////endregion

if($q < 0){
    $m_redis->incr(&#39;rush_fail&#39;);//记录失败的数量
    die($uid.&#39;:decrease fail&#39;);
}else{
    //记录成功的数量
    $m_redis->incr(&#39;rush_success&#39;);
    //记录该会员已购买
    $m_redis->append(&#39;rush_got_uid&#39;, $uid.&#39;,&#39;); //字符串追加
    $m_redis->rPush(&#39;rush_got_uid_l&#39;, $uid); //list
    die($uid.&#39;:success&#39;);
}
登入後複製
###上面的程式碼中的hash保存的會員uid,只是進入搶購隊列的會員uid, 不一定搶購成功了, 那些根本沒有進入搶購隊列的, 也不會在這個hash中, 直接因為庫存為0而被拒絕了.######AB壓力測試:做一個簡單的500個並發Nginx就掛機了)###
Apache路径bin>ab -n 2000 -c 500 http://xxx.com/buy.php
登入後複製
###redis-cli下執行"mget rush_stock rush_fail rush_success rush_got_uid" 確認結果,透過rush_stock 的值查看可能的超發的數量######執行"hvals rush_queue_h"可查看進入搶購隊列的用戶​​id, 這個數量>= 搶購成功的用戶數量######對於list隊列的資料操作, 可以使用 ###BLPOP### 指令, 這樣可以實現FIFO的資料處理順序.###

以上是怎麼用redis做秒殺支撐的demo的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板