>  기사  >  백엔드 개발  >  Redis와 PHP를 결합한 플래시세일 상품에 대한 자세한 설명

Redis와 PHP를 결합한 플래시세일 상품에 대한 자세한 설명

不言
不言원래의
2018-05-09 11:52:344857검색

이 글은 주로 Redis 플래시 판매 제품과 결합된 PHP에 대한 자세한 설명을 소개합니다. 이제 필요한 친구들이 참고할 수 있도록 공유합니다. , 약간의 준비 작업.

1.1

제품 테이블, 주문 테이블

을 생성하고 데이터

주문 테이블을 초기화합니다.


1.2 Redis 대기열에 제품 데이터를 씁니다.

예를 들어 1번 제품이 100개 있습니다. good_1 대기열에 100개의 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는 없습니다.

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

그런 다음 Apache의 ab 가젯을 사용하여 테스트하세요.

-n은 요청 수를 나타냅니다. -c는 한 번에 동시 요청 수를 나타냅니다. 0AB -N 1000 -C 100 http: // xxx


(위 코드에서 트랜잭션을 제거하면 AB 실행 시 폭발적인 주문이 발생하므로 초과근무 문제 세부정보를 클릭하세요. 트랜잭션의 격리를 기반으로 순차적으로 실행되어야 하므로 위의 코드는 과매도 및 주문 폭주 문제를 일으키지 않습니다. (재고 10개 항목 중 11개 항목이 매진되었습니다.) 하지만 이 코드에는 성능 문제가 있습니다. 즉,
동시 요청이 몇 번

있는지, 데이터베이스에

요청되는 횟수

입니다. 취약한 mysql이 빠르게 무너졌습니다.

3 Redis의 플래시 세일 코드를 종료합니다.

드디어 메인 코스가 제공됩니다. . . .

//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;
    }
작은 세부 사항에 주의하세요. 일반적으로 Redis는 캐싱을 위한 프레임워크와 결합됩니다. 위 예시에서 redis 객체 생성 시 별도의 라이브러리를 지정해 주셔야 한다는 점에 유의하시기 바랍니다. (redis에는 일반적으로 9가지 옵션이 있음) 서버가 캐시를 지울 때 데이터가 지워지는 것을 방지합니다.

축하합니다. 위 코드로 기본 버전이 완성되었습니다.
-------------------------------------- -------------------------------------

그러나 이제 이 현상은 운영상의 필요에 따라 계속해서 변화할 것입니다.

예를 들어주세요.

1 해당 상품은 단일 사용자가 3초 이내에 한 번만 구매할 것으로 예상됩니다. 제품 없음

2 제품 단일 사용자가 단일 제품을 최대 5개까지 구매할 수 있기를 바랍니다.

이런 상황이 발생했습니다


질문 1은 다음과 같이 처리됩니다

//用随机值模拟客户,商品,单次购买份数 
        $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는 다음과 같이 수정되었습니다.

//用随机值模拟客户,商品,单次购买份数 
        $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 表的数值
);

이 두 가지 문제는 해결되었으며 다른 유사한 문제도 최대한 빨리 해결될 것입니다. 주의깊은 독자들은 이러한 수정 사항에 공통점이 있다는 것을 발견할 수 있으며, 더 나은 재사용을 위해 코드를 적절하게 캡슐화할 수 있습니다.

관련 권장 사항:

PHP를 Redis와 결합하여 특정 기간 내에 사용자 또는 IP의 방문 횟수를 제한합니다.


PHP를 Linux cron 명령과 결합하여 예약된 작업 예제를 구현합니다

위 내용은 Redis와 PHP를 결합한 플래시세일 상품에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:PHP의 정규 표현식다음 기사:PHP의 정규 표현식