Explication détaillée des produits de vente flash utilisant PHP combiné avec Redis

不言
Libérer: 2023-03-25 17:52:01
original
4889 Les gens l'ont consulté

Cet article présente principalement l'explication détaillée de PHP combinée aux produits de vente flash Redis. Il a une certaine valeur de référence. si vous en avez besoin. Les amis peuvent se référer à

1 Tout d'abord, une petite préparation.

1.1 Créer table de produits, table de commande et initialiser les données

table de commande.


1.2 Écrire les données du produit dans la file d'attente Redis.

Par exemple, il y a 100 pièces du produit n°1. Écrivez simplement 100 1 dans la file d'attente marchandises_1. Par exemple, en utilisant l'atomicité de l'opération pop (transport de concurrence), lors d'un achat ultérieur, il suffit d'en afficher un après en avoir acheté un.

//代码使用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;
Copier après la connexion

Comme indiqué ci-dessous une fois terminé. (Dans des situations réelles, des ajustements de bibliothèque peuvent se produire en arrière-plan et les données dans Redis doivent être synchronisées en conséquence. Veuillez faire attention à cela dans les projets réels, qui ne sont pas présentés ici pour le moment)

2 Lorsqu'il n'y a pas de redis, le code d'achat habituel.

//用随机值模拟客户,商品,单次购买份数 
 $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;
        }
}
Copier après la connexion

Ensuite, utilisez le gadget ab d'Apache pour tester.
-n représente le nombre de requêtes -c représente le nombre de requêtes simultanées à la fois.

<<> AB -N 1000 -C 100 HTTP : // xxx

(Supprimez la transaction dans le code ci-dessus, et il y aura une explosion lorsque AB s'exécutera. )

En raison de l'utilisation du verrouillage de ligne pour la mise à jour. Sur la base de l'isolement des transactions, elles doivent être exécutées séquentiellement, de sorte que le code ci-dessus ne provoquera pas de survente ou d'explosion des commandes. (11 articles vendus sur 10 articles en stock), mais il y a un problème de performances avec ce code, c'est-à-dire combien de fois

il y a demandes simultanées, combien de fois sera demandé au base de données . Le fragile MySQL s’est effondré rapidement.

3 Terminez le code de vente flash de redis.


Enfin, le plat principal est servi. . . .

//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;
    }
Copier après la connexion
Faites attention à un petit détail. Habituellement, redis sera combiné avec le framework de mise en cache. Dans l'exemple ci-dessus, veuillez noter que lors de la création de l'objet redis, spécifiez une bibliothèque distincte. (redis a généralement 9 options) pour éviter d'effacer les données lorsque le serveur vide le cache.

Félicitations, le code ci-dessus a terminé une version de base.

-------------------------------------------------------------- --- ----------------------------------------------- ---
Cependant, le phénomène va continuer à évoluer avec les besoins opérationnels.
Donnez-moi juste un exemple.
1 Le produit ne devrait être acheté par un seul utilisateur qu'une seule fois dans un délai de 3 secondes. Aucun produit

2 produits On espère qu'un seul utilisateur pourra acheter jusqu'à 5 pièces d'un même produit.

Lorsque vous rencontrez cette situation


Le problème 1 est traité comme suit

//用随机值模拟客户,商品,单次购买份数 
        $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("库存不足!");
        }
Copier après la connexion
// Processus post-achat. . . . .

La question 2 est modifiée comme suit.

//用随机值模拟客户,商品,单次购买份数 
        $uid = rand(1,10);
        $amount = rand(1,5);
        $goods_id = rand(1,6);
        $time = time();
   
    $redis = self::createRedisObj();
  ////////////单用户限5个处理///////////////////////////////
        //设一个hash 表, "user_buy"   然后 $uid . "_" . $goods_id 来记录购买的份数。
    $already_num  =  $redis->hGet("user_buy",$uid."_".$goods_id)? $redis->hGet("user_buy",$uid."_".$goods_id)
:0;  //求出已购买份额
      if( $already_num  +$amount > 5){  exit("单用户单个商品限购买5个");}
      else{ 
            $new_num = $already_num  +$amount ;  
          $redis->hSet("user_buy",$uid."_".$goods_id , $new_num);
    }
////////////////////////////////////////////////////////////////////用redis 校验,此次用户是否可以买。(库存是否充足) 
$len = $redis->lLen($redis_key); //求队列的长度,也就是商品的库存。
 if( $len == 0 ){ exit("抢光了!"); }
else if( $len < $amount){ exit("库存不足!"); }// 后序购买流程。。。。。 
//如果购买失败
    $redis->hSet(
    "user_buy",$uid."_".$goods_id , 
  $redis->hGet("user_buy",$uid."_".$goods_id  ) - $amount  //失败时,则回复hash 表的数值
);
Copier après la connexion
Ces deux problèmes ont été traités, et d'autres problèmes similaires seront résolus. Les lecteurs attentifs constateront que de telles modifications ont quelque chose en commun et que le code peut être encapsulé de manière appropriée pour une meilleure réutilisation.


Recommandations associées :

PHP se combine avec Redis pour limiter le nombre de visites par utilisateurs ou IP dans une certaine période de temps

PHP combiné avec la commande Linux cron pour implémenter des exemples de tâches planifiées



Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!