This time I will bring you a detailed explanation of the implementation of PHP's handling of high-concurrency rush-buying requests. What are the precautions for PHP to handle high-concurrency rush-buying requests? The following is a practical case, let's take a look.
This article takes rush sales and flash sales as examples. Introduce how to ensure data accuracy under high concurrency conditions.
It is easy to have two problems with parameters under high concurrent requests
1. Data errors lead to oversold products.
2. Frequent operations on the database result in performance degradation.
Test environment
Windows7
apache2.4.9
php5.5.12
php framework yii2.0
Tool apache bench (apache since With high concurrent request tools).
Usual processing method
The code idea can be seen from the controller. Check product inventory first. If the inventory is greater than 0, the inventory is reduced by 1, and orders are produced at the same time, and rush buyer data is entered.
// 常规代码处理高并发 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 '恭喜你,订单<b>'.$order.'</b>抢购成功'; } }else{ echo '已被抢购一空!'; } }
After setting the product inventory to 20, configure concurrent requests of 200 through ab.
ab -n 200 -c 200 http//localhost/highly/normal
The execution result found that the inventory became negative and the product was oversold.
The reason is relatively simple, under high concurrent requests. Before producing orders and reducing inventory, the inventory results will be queried first.
Optimization 1: Modify inventoryData type
The first optimization method starts with the database. Since the query results are inaccurate, I will try to reduce the inventory. Change the data type of the inventory to unsigned (cannot have negative values).
The code is still similar to the above, except that a judgment is made where the inventory is reduced by 1. Avoid reporting errors.
public function actionNormal(){ // 查询库存 $stock = Goods::find()->select('stock')->where(['goods_id'=>100001])->asArray()->one(); // 判断该商品是否还有库存 if ($stock['stock']>0) { // 库存减一 if(Goods::updateAllCounters(['stock' => -1],['goods_id'=>100001])===false){ echo "已被抢购一空!"; return false; } // 生产订单(另外功能,暂且随机赋值) $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 '恭喜你,订单<b>'.$order.'</b>抢购成功'; } }else{ echo '已被抢购一空!'; } }
This time the same concurrency of 200 was found, and the execution results were found. The data is correct and there is no oversold situation.
The idea is actually relatively simple. Because the inventory cannot be negative, when the inventory is equal to 0, if there are still values passed in, an error will be reported. The request was terminated.
This optimization method avoids the oversold situation of products. But on the other hand, requests still put pressure on the database. If multiple functions use this database, performance will drop significantly.
Optimization 2: redis
Use the atomicity of redis list type pop. Before operating the database, do a verification. When the goods are sold out, no further database operations are allowed.
// redis list 高并发测试 public function actionRedis(){ $redis = \Yii::$app->redis; // $redis->lpush('mytest',1); $order = $this->build_order(); // echo $order;die; // echo $redis->llen('mytest'); $reg = $redis->lpop('mytest'); if (!$reg) { echo "笨蛋!已经被抢光啦!"; return false; } $redis->close(); $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 '恭喜你,订单<b>'.$order.'</b>抢购成功'; } } // 给redis添加商品 public function actionInsertgoods(){ $count = yii::$app->request->get('count',0); if (empty($count)) { echo '大兄弟,你还没告诉我需要上架多少商品呢!'; return false; } $redis = \Yii::$app->redis; for ($i=0; $i < $count; $i++) { $redis->lpush('mytest',1); } echo '成功添加了'.$redis->llen('mytest').'件商品。'; $redis->close(); }
For this code, I wrote two methods. The first method is the flash sale code, and the second method is to set the quantity for the flash sale products. In order to facilitate testing, what I handle here is relatively simple.
Through the test, the number of orders produced by the database is normal and there are no problems. This avoids the problem of performance degradation caused by requesting the database. At the same time, the query speed of the in-memory database redis is much faster than that of mysql.
I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!
Recommended reading:
php Ajax non-refresh file upload implementation steps detailed explanation
What are the multi-dimensional array sorting algorithms for PHP Way
The above is the detailed content of Detailed explanation of how PHP handles high-concurrency requests for rush purchases. For more information, please follow other related articles on the PHP Chinese website!