首頁 > 後端開發 > php教程 > 使用redis秒殺出現產品超發現象求解?

使用redis秒殺出現產品超發現象求解?

WBOY
發布: 2016-10-17 09:30:08
原創
1221 人瀏覽過

最近在做一個秒殺活動,處於性能和響應速度的考慮,使用了redis。寫的時候就特別注意了杜絕超發現象,基於redis理論的cas(check and set)樂觀鎖,想著應該能夠杜絕該問題,但是還是出現了,很疑惑求大神幫助,具體的代碼大致如下:

<code><?php  
header("content-type:text/html;charset=utf-8");  
$redis = new redis();  
$result = $redis->connect('10.10.10.119', 6379);  
$mywatchkey = $redis->get("mywatchkey");  
$rob_total = 100;   //抢购数量  
if($mywatchkey<$rob_total){  
    $redis->watch("mywatchkey");  
    $redis->multi();  
      
    //设置延迟,方便测试效果。  
    sleep(5);  
    //插入抢购数据  
    $redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());  
    $redis->set("mywatchkey",$mywatchkey+1);  
    $rob_result = $redis->exec();  
    if($rob_result){  
        $mywatchlist = $redis->hGetAll("mywatchlist");  
        echo "抢购成功!<br/>";  
        echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>";  
        echo "用户列表:<pre class="brush:php;toolbar:false">";  
        var_dump($mywatchlist);  
    }else{  
        echo "手气不好,再抢购!";exit;  
    }  
}  
?>  
</code>
登入後複製
登入後複製

回覆內容:

最近在做一個秒殺活動,處於性能和響應速度的考慮,使用了redis。寫的時候就特別注意了杜絕超發現象,基於redis理論的cas(check and set)樂觀鎖,想著應該能夠杜絕該問題,但是還是出現了,很疑惑求大神幫助,具體的代碼大致如下:

<code><?php  
header("content-type:text/html;charset=utf-8");  
$redis = new redis();  
$result = $redis->connect('10.10.10.119', 6379);  
$mywatchkey = $redis->get("mywatchkey");  
$rob_total = 100;   //抢购数量  
if($mywatchkey<$rob_total){  
    $redis->watch("mywatchkey");  
    $redis->multi();  
      
    //设置延迟,方便测试效果。  
    sleep(5);  
    //插入抢购数据  
    $redis->hSet("mywatchlist","user_id_".mt_rand(1, 9999),time());  
    $redis->set("mywatchkey",$mywatchkey+1);  
    $rob_result = $redis->exec();  
    if($rob_result){  
        $mywatchlist = $redis->hGetAll("mywatchlist");  
        echo "抢购成功!<br/>";  
        echo "剩余数量:".($rob_total-$mywatchkey-1)."<br/>";  
        echo "用户列表:<pre class="brush:php;toolbar:false">";  
        var_dump($mywatchlist);  
    }else{  
        echo "手气不好,再抢购!";exit;  
    }  
}  
?>  
</code>
登入後複製
登入後複製

我覺得這個程式碼在高並發的情況,還是會出現超賣現象。假如:只剩下 1個獎品的時候,有三個人同時執行 $redis->watch("mywatchkey") 拿到的資料是 99 ,那麼就出現超賣現象了。

由於redis是單執行緒讀取,那就用最簡單的佇列實作吧。

  1. 在抽獎前先把獎品數量,寫入redis隊列award:100 // 長度為100的 list ,值只是作為是否中獎

  2. 並發抽獎

<code>$award = $redis->lpop('award:100'); // 由于队列只有100个值,可以确保只有100个人中奖
if(!$award){
    echo "手气不好,再抢购!";exit;  
}

// 剩下就是中奖操作的事情了</code>
登入後複製

sleep(5);
你公司還收不收人?

考慮一下這種情況。
mywatchkey=99
用戶A請求mywatchkey得到99,
用戶B請求mywatchkey得到99,
當A,B請求完成之後,mywatchkey應該為多少呢。 。 101,還是100?

你這個是經典的Check-then-Act錯誤

先問一下你這個程式碼的「超賣」結果是$mywatchkey > 100了(我認為不可能出現)
還是$mywatchkey =100時候同時有兩個頁面出現「搶購成功!「
我沒有對這個代碼進行測試,我只是猜測會不會是以下的情況:

1.Session A : 進行mywatchkey檢查,此時mywatchkey = 99

2.Session B : 處理完 mywatchkey以後,此時mywatchkey=100 但是不影響Session A的後續操作

補充兩個連接,看看吧,或許有收穫

transactions-and-watch-statement-in-redis
redis-watch-multi-exec-by-one-client

如果這事要從根本解決,請先學習:

1.鎖。

2.傳統資料庫事務。

接著再學習Redis與傳統資料庫的差別。

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