php處理搶購類高並發請求實現詳解

來源:互聯網
上載者:User
這次給大家帶來php處理搶購類高並發請求實現詳解,php處理搶購類高並發請求的注意事項有哪些,下面就是實戰案例,一起來看一下。

本文以搶購、秒殺為例。介紹如何在高並髮狀況下確保資料正確。
在高並發請求下容易參數兩個問題
1.資料出錯,導致產品超賣。
2.頻繁操作資料庫,導致效能下降。

測試環境

Windows7
apache2.4.9
php5.5.12
php架構 yii2.0
工具 apache bench (apache內建高並發請求工具)。

通常處理方法

從控制器可以看出代碼思路。先查詢商品庫存。如果庫存大於0 ,則庫存減少1,同時生產訂單,錄入搶購者資料。

// 常規代碼處理高並發  public function actionNormal(){    // 查詢庫存    $stock = Goods::find()->select('stock')->where(['goods_id'=>100001])->asArray()->one();    // 判斷該商品是否還有庫存    if ($stock['stock']>0) {      // 庫存減一      Goods::updateAllCounters(['stock' => -1],['goods_id'=>100001]);      // 生產訂單(另外功能,暫且隨機賦值)      $order = $this->build_order();      // 秒殺資訊入庫      $model = new Highly();      $model->order_id = $order;      $model->goods_name = '秒殺商品';      $model->buy_time = date('Y-m-d H:i:s',time());      $model->mircrotime = microtime(true);      if($model->save()===false){        echo '未能成功搶購!';      }else{        echo '恭喜你,訂單<b>'.$order.'</b>搶購成功';      }    }else{      echo '已被搶購一空!';    }  }

將商品庫存設定為20後,通過ab 配置200的並發請求。

ab -n 200 -c 200 http//localhost/highly/normal

執行結果發現庫存變成了負值,商品超賣了。

原因比較簡單,在高並發請求下。在生產訂單,減少庫存之前,會優先查詢到庫存結果。

最佳化一:修改庫存資料類型

第一種最佳化方法,從資料庫入手。既然查詢到的結果不準確,那我就在庫存減少上做手腳。將庫存的資料類型改成無符號(不能有負值)。

代碼還是跟上面差不多,只是在庫存減1的地方做了個判斷。避免報錯。

public function actionNormal(){    // 查詢庫存    $stock = Goods::find()->select('stock')->where(['goods_id'=>100001])->asArray()->one();    // 判斷該商品是否還有庫存    if ($stock['stock']>0) {      // 庫存減一      if(Goods::updateAllCounters(['stock' => -1],['goods_id'=>100001])===false){        echo "已被搶購一空!";        return false;      }      // 生產訂單(另外功能,暫且隨機賦值)      $order = $this->build_order();      // 秒殺資訊入庫      $model = new Highly();      $model->order_id = $order;      $model->goods_name = '秒殺商品';      $model->buy_time = date('Y-m-d H:i:s',time());      $model->mircrotime = microtime(true);      if($model->save()===false){        echo '未能成功搶購!';      }else{        echo '恭喜你,訂單<b>'.$order.'</b>搶購成功';      }    }else{      echo '已被搶購一空!';    }  }

這一次同樣200的並發,執行結果發現。資料正確,並不會出現超賣的情況。
思路其實也比較簡單。因為庫存不能為負值,當庫存等於0時,如果還有值傳進來,則會報錯。請求被終止。

這種最佳化方式,雖然避免了商品超賣的情況。但是在另一方面,請求仍然會對資料庫造成壓力。如果多個功能使用此資料庫,會造成效能下降厲害。

最佳化二:redis

利用 redis list類型的pop的原子性。在操作資料庫前,做一個驗證。當商品賣完後,就不允許再繼續進行資料庫操作。

// redis list 高並發測試  public function actionRedis(){    $redis = \Yii::$app->redis;    // $redis->lpush('mytest',1);    $order = $this->build_order();    // echo $order;die;    // echo $redis->llen('mytest');    $reg = $redis->lpop('mytest');    if (!$reg) {      echo "笨蛋!已經被搶光啦!";      return false;    }    $redis->close();    $model = new Highly();    $model->order_id = $order;    $model->goods_name = '秒殺商品';    $model->buy_time = date('Y-m-d H:i:s',time());    $model->mircrotime = microtime(true);    if($model->save()===false){      echo '未能成功搶購!';    }else{      echo '恭喜你,訂單<b>'.$order.'</b>搶購成功';    }  }  // 給redis添加商品  public function actionInsertgoods(){    $count = yii::$app->request->get('count',0);    if (empty($count)) {      echo '大兄弟,你還沒告訴我需要上架多少商品呢!';      return false;    }    $redis = \Yii::$app->redis;    for ($i=0; $i < $count; $i++) {       $redis->lpush('mytest',1);    }    echo '成功添加了'.$redis->llen('mytest').'件商品。';    $redis->close();  }

這點的代碼,我寫了兩個方法。第一個方法是秒殺的代碼,第二個方法是給秒殺的商品設定數量。為了方便測試,我這裡處理的比較簡單。

通過測試,資料庫生產的訂單數量正常,並沒有出現問題。而又避免了請求資料庫造成效能下降的問題。同時記憶體資料庫redis查詢的速度要比mysql快很多。

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

php+ajax無重新整理檔案上傳實現步驟詳解

PHP實現多維陣列排序演算法有哪些方式

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.