php處理搶購類功能的高并發(fā)請(qǐng)求
本文以搶購、秒殺為例。介紹如何在高并發(fā)狀況下確保數(shù)據(jù)正確。
在高并發(fā)請(qǐng)求下容易參數(shù)兩個(gè)問題
1.數(shù)據(jù)出錯(cuò),導(dǎo)致產(chǎn)品超賣。
2.頻繁操作數(shù)據(jù)庫,導(dǎo)致性能下降。
測(cè)試環(huán)境
Windows7
apache2.4.9
php5.5.12
php框架 yii2.0
工具 apache bench (apache自帶高并發(fā)請(qǐng)求工具)。
通常處理方法
從控制器可以看出代碼思路。先查詢商品庫存。如果庫存大于0 ,則庫存減少1,同時(shí)生產(chǎn)訂單,錄入搶購者數(shù)據(jù)。
// 常規(guī)代碼處理高并發(fā) 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]); // 生產(chǎn)訂單(另外功能,暫且隨機(jī)賦值) $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 '已被搶購一空!'; } }
將商品庫存設(shè)置為20后,通過ab 配置200的并發(fā)請(qǐng)求。
ab -n 200 -c 200 http//localhost/highly/normal
執(zhí)行結(jié)果發(fā)現(xiàn)庫存變成了負(fù)值,商品超賣了。
原因比較簡(jiǎn)單,在高并發(fā)請(qǐng)求下。在生產(chǎn)訂單,減少庫存之前,會(huì)優(yōu)先查詢到庫存結(jié)果。
優(yōu)化一:修改庫存數(shù)據(jù)類型
第一種優(yōu)化方法,從數(shù)據(jù)庫入手。既然查詢到的結(jié)果不準(zhǔn)確,那我就在庫存減少上做手腳。將庫存的數(shù)據(jù)類型改成無符號(hào)(不能有負(fù)值)。
代碼還是跟上面差不多,只是在庫存減1的地方做了個(gè)判斷。避免報(bào)錯(cuò)。
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; } // 生產(chǎn)訂單(另外功能,暫且隨機(jī)賦值) $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 '已被搶購一空!'; } }
這一次同樣200的并發(fā),執(zhí)行結(jié)果發(fā)現(xiàn)。數(shù)據(jù)正確,并不會(huì)出現(xiàn)超賣的情況。
思路其實(shí)也比較簡(jiǎn)單。因?yàn)閹齑娌荒転樨?fù)值,當(dāng)庫存等于0時(shí),如果還有值傳進(jìn)來,則會(huì)報(bào)錯(cuò)。請(qǐng)求被終止。
這種優(yōu)化方式,雖然避免了商品超賣的情況。但是在另一方面,請(qǐng)求仍然會(huì)對(duì)數(shù)據(jù)庫造成壓力。如果多個(gè)功能使用此數(shù)據(jù)庫,會(huì)造成性能下降厲害。
優(yōu)化二:redis
利用 redis list類型的pop的原子性。在操作數(shù)據(jù)庫前,做一個(gè)驗(yàn)證。當(dāng)商品賣完后,就不允許再繼續(xù)進(jìn)行數(shù)據(jù)庫操作。
// redis list 高并發(fā)測(cè)試 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 "笨蛋!已經(jīng)被搶光啦!"; 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(); }
這點(diǎn)的代碼,我寫了兩個(gè)方法。第一個(gè)方法是秒殺的代碼,第二個(gè)方法是給秒殺的商品設(shè)置數(shù)量。為了方便測(cè)試,我這里處理的比較簡(jiǎn)單。
通過測(cè)試,數(shù)據(jù)庫生產(chǎn)的訂單數(shù)量正常,并沒有出現(xiàn)問題。而又避免了請(qǐng)求數(shù)據(jù)庫造成性能下降的問題。同時(shí)內(nèi)存數(shù)據(jù)庫redis查詢的速度要比mysql快很多。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- PHP并發(fā)場(chǎng)景的三種解決方案代碼實(shí)例
- php并發(fā)加鎖問題分析與設(shè)計(jì)代碼實(shí)例講解
- PHP解決高并發(fā)的優(yōu)化方案實(shí)例
- php多進(jìn)程模擬并發(fā)事務(wù)產(chǎn)生的問題小結(jié)
- PHP利用Mysql鎖解決高并發(fā)的方法
- php curl批處理實(shí)現(xiàn)可控并發(fā)異步操作示例
- PHP+Redis 消息隊(duì)列 實(shí)現(xiàn)高并發(fā)下注冊(cè)人數(shù)統(tǒng)計(jì)的實(shí)例
- PHP開發(fā)中解決并發(fā)問題的幾種實(shí)現(xiàn)方法分析
- PHP使用Redis實(shí)現(xiàn)防止大并發(fā)下二次寫入的方法
- php結(jié)合redis高并發(fā)下發(fā)帖、發(fā)微博的實(shí)現(xiàn)方法
- 詳解php處理大并發(fā)大流量大存儲(chǔ)
相關(guān)文章
PHP 獲取MSN好友列表的代碼(2009-05-14測(cè)試通過)
感謝druggo提供代碼 放到首頁只是覺得值得大家研究 等有機(jī)會(huì)把這代碼裝成jsp的2009-09-09PHP運(yùn)用foreach神奇的轉(zhuǎn)換數(shù)組(實(shí)例講解)
下面小編就為大家分享一篇PHP運(yùn)用foreach神奇的轉(zhuǎn)換數(shù)組(實(shí)例講解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02PHP利用超級(jí)全局變量$_GET來接收表單數(shù)據(jù)的實(shí)例
下面小編就為大家?guī)硪黄狿HP利用超級(jí)全局變量$_GET來接收表單數(shù)據(jù)的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-11-11php實(shí)現(xiàn) master-worker 守護(hù)多進(jìn)程模式的實(shí)例代碼
這篇文章主要介紹了php實(shí)現(xiàn) master-worker 守護(hù)多進(jìn)程模式的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-07-07ThinkPHP關(guān)于session的操作方法匯總
這篇文章主要介紹了ThinkPHP關(guān)于session的操作方法,有助于讀者加深對(duì)ThinkPHP操作session的認(rèn)識(shí),需要的朋友可以參考下2014-07-07swoole_process實(shí)現(xiàn)進(jìn)程池的方法示例
這篇文章主要介紹了swoole_process實(shí)現(xiàn)進(jìn)程池的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10