用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" 確認初始化資料
判斷的邏輯:
目前是用string int存儲庫存, 也可以用list的item的個數來計數, 但是初始化時沒有string類型來得簡單.
//随机生成会员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('rush_fail');//记录失败的数量 die($uid.':OutOfStock'); } //2. 判断该会员是否购买过 => 是否进入过队列 $queued = $m_redis->hSet('rush_queue_h', $uid, $uid);//这里只能判断是否进入了抢购的队列. 如果库存为0则无法进入. 进入了队列后才能抢购 if(!$queued){ $m_redis->incr('rush_fail');//记录失败的数量 die($uid.':queue failed'); } //让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.' left:'; ////region 如果不判断操作后返回的结果,则可能会造成超发 //$m_redis->incr('q_success');//记录成功的数量 ==>这个是有bug的, 不可取 //die(':success'); ////endregion if($q < 0){ $m_redis->incr('rush_fail');//记录失败的数量 die($uid.':decrease fail'); }else{ //记录成功的数量 $m_redis->incr('rush_success'); //记录该会员已购买 $m_redis->append('rush_got_uid', $uid.','); //字符串追加 $m_redis->rPush('rush_got_uid_l', $uid); //list die($uid.':success'); }
Apache路径bin>ab -n 2000 -c 500 http://xxx.com/buy.php
以上是怎麼用redis做秒殺支撐的demo的詳細內容。更多資訊請關注PHP中文網其他相關文章!