首頁  >  文章  >  後端開發  >  php如何處理搶購類別功能的高並發請求

php如何處理搶購類別功能的高並發請求

小云云
小云云原創
2018-02-09 09:15:582437瀏覽

在高並發請求下容易參數兩個問題 

1.資料出錯,導致產品超賣。 

2.頻繁操作資料庫,導致效能下降。

本文主要和大家詳細介紹了php處理搶購類功能的高並發請求,具有一定的參考價值,有興趣的小伙伴們可以參考一下,希望能幫助到大家。

測試環境

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 &#39;恭喜你,订单<b>&#39;.$order.&#39;</b>抢购成功&#39;;
      }

    }else{
      echo &#39;已被抢购一空!&#39;;
    }
  }

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


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

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

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

優化一:修改庫存資料類型

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

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


public function actionNormal(){
    // 查询库存
    $stock = Goods::find()->select(&#39;stock&#39;)->where([&#39;goods_id&#39;=>100001])->asArray()->one();
    // 判断该商品是否还有库存
    if ($stock[&#39;stock&#39;]>0) {
      // 库存减一
      if(Goods::updateAllCounters([&#39;stock&#39; => -1],[&#39;goods_id&#39;=>100001])===false){
        echo "已被抢购一空!";
        return false;
      }

      // 生产订单(另外功能,暂且随机赋值)
      $order = $this->build_order();

      // 秒杀信息入库
      $model = new Highly();
      $model->order_id = $order;
      $model->goods_name = &#39;秒杀商品&#39;;
      $model->buy_time = date(&#39;Y-m-d H:i:s&#39;,time());
      $model->mircrotime = microtime(true);
      if($model->save()===false){
        echo &#39;未能成功抢购!&#39;;
      }else{
        echo &#39;恭喜你,订单<b>&#39;.$order.&#39;</b>抢购成功&#39;;
      }

    }else{
      echo &#39;已被抢购一空!&#39;;
    }
  }

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

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

優化二:redis

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


// redis list 高并发测试
  public function actionRedis(){
    $redis = \Yii::$app->redis;
    // $redis->lpush(&#39;mytest&#39;,1);
    $order = $this->build_order();
    // echo $order;die;
    // echo $redis->llen(&#39;mytest&#39;);
    $reg = $redis->lpop(&#39;mytest&#39;);
    if (!$reg) {
      echo "笨蛋!已经被抢光啦!";
      return false;
    }
    $redis->close();
    $model = new Highly();
    $model->order_id = $order;
    $model->goods_name = &#39;秒杀商品&#39;;
    $model->buy_time = date(&#39;Y-m-d H:i:s&#39;,time());
    $model->mircrotime = microtime(true);

    if($model->save()===false){
      echo &#39;未能成功抢购!&#39;;
    }else{
      echo &#39;恭喜你,订单<b>&#39;.$order.&#39;</b>抢购成功&#39;;
    }
  }
  // 给redis添加商品
  public function actionInsertgoods(){
    $count = yii::$app->request->get(&#39;count&#39;,0);
    if (empty($count)) {
      echo &#39;大兄弟,你还没告诉我需要上架多少商品呢!&#39;;
      return false;
    }
    $redis = \Yii::$app->redis;
    for ($i=0; $i < $count; $i++) { 
      $redis->lpush(&#39;mytest&#39;,1);
    }
    echo &#39;成功添加了&#39;.$redis->llen(&#39;mytest&#39;).&#39;件商品。&#39;;
    $redis->close();

  }

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

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

相關推薦:

node單執行緒實作高並發原理

PHP和Redis實作高並發下註冊人數統計

php和redis實現高並發下的搶購以及秒殺功能範例詳解

以上是php如何處理搶購類別功能的高並發請求的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn