This article mainly introduces the detailed explanation of PHP combined with redis flash sale products. It has certain reference value. Now I will share it with you if you need it. Friends can refer to
1.1 Create product table, order table , and initialize data
order table.
#1.2 Write product data to the redis queue.
For example, there are 100 pieces of product No. 1. Just write 100 1's into the goods_1 queue. For example, using the atomicity of the pop operation (carrying concurrency), when purchasing later, just pop one after buying one.
//代码使用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;
As shown below after completion. (In real situations, there may be library adjustments in the background, and the data in redis needs to be synchronized accordingly. Please pay attention to it in actual projects, which will not be shown here for the time being)
//用随机值模拟客户,商品,单次购买份数 $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 = '$sn',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; } }
Then use apache’s ab gadget to test.
-n represents the number of requests -c represents the number of concurrent requests at a time.
ab -n 1000 -c 100 http://xxx
(Remove the transactions in the above code, and then when ab is run, there will be burst orders and oversold problems. Click for details )
Due to the use of row lock for update. Based on the isolation of transactions, it must be executed sequentially, so the above code will not cause the problem of overselling and exploding orders. (11 items were sold out of 10 items in stock), but there is a performance problem with this code, that is, how many concurrent requests there are, how many requests will be made to the database . The fragile mysql collapsed quickly.
Finally the main course is served. . . .
//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; }
Pay attention to a small detail. Usually redis will be combined with the framework for caching. In the above example, please note that when creating the redis object, specify a separate library. (redis generally has 9 options) to avoid clearing the data when the server clears the cache.
Congratulations, the above code has completed a basic version.
-------------------------------------------------- --------------------------------------------------
However, the phenomenon will continue to change with operational needs.
Just give me an example.
1 The product is expected to be purchased by a single user only once within 3 seconds. No product
2 Product It is hoped that a single user can buy up to 5 pieces of a single product.
Encountered this situation
Problem 1 is handled as follows
//用随机值模拟客户,商品,单次购买份数 $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("库存不足!"); }
// Post-order purchase process. . . . .
Question 2 is modified as follows.
//用随机值模拟客户,商品,单次购买份数 $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 表的数值 );
After these two problems have been dealt with, other similar problems will be resolved as soon as possible. Careful readers can find that such modifications have something in common, and the code can be appropriately encapsulated for better reuse.
Related recommendations:
PHP Combined with Linux cron command to implement scheduled task example
The above is the detailed content of Detailed explanation of flash sale products using PHP combined with redis. For more information, please follow other related articles on the PHP Chinese website!