ホームページ  >  記事  >  バックエンド開発  >  PHPとredisを組み合わせたフラッシュセール商品の詳細説明

PHPとredisを組み合わせたフラッシュセール商品の詳細説明

不言
不言オリジナル
2018-05-09 11:52:344857ブラウズ

この記事では、主に Redis フラッシュセール製品と組み合わせた PHP の詳細な説明を紹介します。必要な友人に参考にしてください。 、少し準備作業。

1.1

productテーブル、orderテーブル

を作成し、

orderテーブルのデータを初期化します。

1.2 製品データを Redis キューに書き込みます。

例えば、商品No.1は100個あります。 100 個の 1 を Goods_1 キューに書き込むだけです。たとえば、ポップ操作の原子性 (同時実行性の保持) を使用して、後で購入する場合は、1 つ購入した後に 1 つポップするだけです。

//代码使用yii 框架,重点在思路,其它框架做少量调整即可。 
 $redis = self::createRedisObj(); //创建redis 对象,后面提供详细代码

        $sql = "select * from sec_goods";
        $rows = Yii::app()->db->createCommand($sql)->queryAll();

        foreach( $rows as $key => $row ):
            $goods_id = $row["id"];
            $stock_avail =  $row["stock_avail"];
            $redis_key = "goods_".$goods_id;
            for($i =0 ; $i< $stock_avail; $i++){
                $redis->lpush($redis_key , 1);
            }
            echo $goods_id."llen is ".$redis->lLen($redis_key)."<br/>";
        endforeach;
完成後は以下の通り。 (実際の状況では、バックグラウンドでライブラリの調整が行われる可能性があり、それに応じて Redis 内のデータを同期する必要があります。実際のプロジェクトでは注意してください。現時点ではここには示されていません)

2 Redis がない場合は、通常の購入コード。

そうです

次に、Apache の ab ガジェットを使用してテストします。

-n はリクエストの数を表します。 -c は一度に同時リクエストの数を表します。 0AB -N 1000 -C 100 http://xxx

(上記のコードのトランザクションを削除すると、AB が実行されると爆発的な注文が発生します。時間外問題の詳細をクリックしてください。 トランザクションの分離に基づいて、トランザクションは順次実行される必要があるため、上記のコードは過剰販売や注文の爆発を引き起こすことはありません。 (在庫 10 個のうち 11 個が売り切れました) しかし、このコードにはパフォーマンスの問題があります。つまり、
同時リクエストが何回あるか、データベース
に何回

リクエストが行われるかということです。脆弱な mysql はすぐに崩壊しました。

3 Redisのフラッシュセールコードを終了します。 いよいよメイン料理の提供です。 。 。 。

//用随机值模拟客户,商品,单次购买份数 
 $uid = rand(1,10);
        $amount = rand(1,5);
        $goods_id = rand(1,6);
        $time = time();
$this->buy($uid , $goods_id , $amount);

public function buy($uid , $goods_id , $amount){

     //使用行锁.
        try {
            $trans = Yii::app()->db->beginTransaction();

            $sql = "select stock_avail from sec_goods where id = $goods_id for update";  //
            $stock_avail = Yii::app()->db->createCommand($sql)->queryScalar();

            if( $stock_avail >= $amount ){  //份额足够。
                $sn = date("YmdHis")."-".$uid."-".$goods_id.rand(1000,9999);

                $sql = "insert into sec_order set sn = &#39;$sn&#39;,user_id = $uid, goods_id = $goods_id, create_at = $time,num = $amount";
                $bool = Yii::app()->db->createCommand($sql)->execute();
                if( !$bool ){ throw new Exception("执行失败".$sql); }

                $sql = "update sec_goods set stock_avail = stock_avail - $amount  where id= $goods_id";
                $bool = Yii::app()->db->createCommand($sql)->execute();
                if( !$bool ){ throw new Exception("执行失败".$sql); }
            }
            $trans->commit();
            return true;
        } catch (Exception $e) {
            //日志记录
            $trans->rollback();
            return false;
        }
}

通常、redis はキャッシュ用のフレームワークと結合されます。上記の例では、redis オブジェクトを作成するときに別のライブラリを指定することに注意してください。 (redis には通常 9 つのオプションがあります) サーバーがキャッシュをクリアするときにデータがクリアされないようにします。 おめでとうございます。上記のコードで基本バージョンが完成しました。

------------------------------------------------- --------------------------------------------------

しかし現在、この現象は運用上のニーズに応じて変化し続けるでしょう。
例を挙げてみましょう。

1 製品は 1 人のユーザーによって 3 秒以内に 1 回のみ購入されることが想定されます。 商品はありません

2 商品 1 人のユーザーが 1 つの商品を最大 5 個まで購入できることを希望します。

この状況に遭遇した場合


問題 1 は次のように処理されます

//code 3.1   
//用随机值模拟客户,商品,单次购买份数 
       $uid = rand(1,10);
$amount = rand(1,5); 
$goods_id = rand(1,6);
 $time = time(); 

//用redis 校验,此次用户是否可以买。(库存是否充足)
 $redis = self::createRedisObj(); 
$redis_key = "goods_".$goods_id; 
$len = $redis->lLen($redis_key); //求队列的长度,也就是商品的库存。 
if( $len == 0 ){ exit("抢光了!"); }
else if( $len < $amount){ exit("库存不足!"); }

//验证通过,开始pop 出队列。  pop 一个,相当于买一个。  
        for( $i =0 ; $i<  $amount;$i++){
             $redis->rPop( $redis_key );
        }

        $bool = $this->buy($uid , $goods_id , $amount);
        if( !$bool ) {   //如果购买失败,则把取出的redis 队列的数据,再压回去。(回充库存)
         for( $i =0 ; $i<  $amount;$i++){
             $redis->lPush( $redis_key , 1);
         }
     }
//创建redis 对象的。
  private static $_redis = null;
    /**
     * 创建一个redis 对象.
     * @return Redis
     */
    public static function createRedisObj(){ //2017-11-29 改为单例创建模式.

        if( ! self::$_redis){
            $redis = new Redis();
            $host = “192.168.1.xx”;
            $port = "6379";
            $redis->connect($host,$port);
            self::$_redis = $redis;
        }
        return self::$_redis;
    }

// 購入後のプロセス。 。 。 。 。

質問2は次のように修正されます。

//用随机值模拟客户,商品,单次购买份数 
        $uid = rand(1,10);
        $amount = rand(1,5);
        $goods_id = rand(1,6);
        $time = time();
   
    $redis = self::createRedisObj();
  ////////////单用户限3秒内仅允许请求一次///////////////////////////////
       $lock_key = "uTimeLimit_".$uid;  
      //按用户名编即可。  如果限用户针对指定商品,则lock_key 按uid+ goods_id 进行唯一编码
      if( $redis->get($lock_key)){
            $left_time = $redis->ttl($lock_key);
            exit($expire ."秒内只允许 $tag 一次!请".$left_time."之后再尝试");
        }else {
            $redis->setEx($lock_key ,  $expire , "1" );
        }
//////////////////////////////////////////////////////////////////
//用redis 校验,此次用户是否可以买。(库存是否充足)
  $len = $redis->lLen($redis_key);  //求队列的长度,也就是商品的库存。
        if( $len == 0 ){
            exit("抢光了!");
        }else if( $len < $amount){
            exit("库存不足!");
        }

これら 2 つの問題はすでに解決されており、他の同様の問題もできるだけ早く解決される予定です。注意深い読者であれば、このような変更には共通点があり、再利用しやすくするためにコードを適切にカプセル化できることがわかります。

関連する推奨事項:

PHP を Redis と組み合わせて、一定期間内のユーザーまたは IP によるアクセス数を制限する


PHP を Linux cron コマンドと組み合わせて、スケジュールされたタスクの例を実装する

以上がPHPとredisを組み合わせたフラッシュセール商品の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:Phpの正規表現次の記事:Phpの正規表現