秒殺問題主要涉及到的是 指定商品的數量還有突然的併發
先展示下正常的寫法
//查詢商品數量 $goodsNum = 10
if ($goodsNum > 0) {
//對商品數量進行自減1
if (自減成功) {
//將用戶添加到搶購成功表中
}else{
//搶購失敗
}
}else{
//商品搶完了
}
這樣寫可以實現功能,前提是沒有任何併發的時候,如果此時有100個用戶同時請求,有可能會有20多個請求同時到達if裏面,這樣就導致了商品數量變爲負數。
所以這個時候我們不單單使用mysql,再加上redis中的隊列做處理。因爲redis支持單線程,所以使用redis中隊列,保持隊列中元素先進先出,每次只出一個。
首先,先將商品加入到隊列中。
$redis = new redis();
$redis->connect("127.0.0.1","6379");
//先將商品數量寫入到隊列中
//商品數量
$goodsNum = 10;
//商品列表隊列的redis
$goodsList = "goodsList_1";
//將商品依次加入隊列中
for ($i=1; $i <= $goodsNum; $i++) {
$redis->lpush($goodsList,1);
}
接下來就是商品的搶購了
$redis = new redis();
$redis->connect("127.0.0.1","6379");
//先將商品數量寫入到隊列中
//商品列表隊列的redis
$goodsList = "goodsList_1";
//模擬用戶id
$userid = rand(1000,9999);
//搶到商品的用戶集合
$userList = 'userList_1';
//出隊列
if (!$redis->rpop($goodsList)) {
//沒有商品 已經搶購完了
}
//將搶到的商品的用戶放進一個集合中
if ($redis->lpush($userList,$userid)) {
//搶購成功
}else{
//搶購失敗
}
這樣基本就可以實現秒殺功能。但是如果該商品只允許用戶搶購成功一次,那還是有點瑕疵。所以在此改進。
//所有搶購的用戶集合
$waitUserList = "waitUserList_1";
//搶購成功的用戶集合
$userList = "userList_1";
//模擬用戶
$userid = rand(1000,9999);
//商品列表隊列的redis
$goodsList = "goodsList_1";
//將所有進來的用戶存入
if ($redis->hset($waitUserList,$userid,$userid)) {
if ($redis->rpop($goodsList)) {
//將用戶存入搶購成功集合
$redis->hset($userList,$userid,$userid);
//搶購成功
}else{
//商品搶完了
}
}
使用hset存用戶是因爲hash裏的值不允許重複,如果裏面有了鍵值是1的,此時hset就存不進去。