這是一篇真實案例,並非理論課,阿北將同步個人整個優化之路,優化之路慢慢長,對你們拋磚引玉已達目的,若你也有一些優化思路,請跟貼。php
項目是年前一個朋友作的,客戶也是個人一個朋友,因此如今來幫忙優化,系統很簡單,就是一個菜單頁面,客戶下單,而後打印機出小票,整個系統使用yii2基礎版 + MySQL5.6.29驅動。html
客戶店裏天天大約走1.5-2w的流水,如今最大的表有26w數據,我切圖你們先看下。web
從圖例看可能要優化的地方數據庫
表引擎使用了MyISAM問題緩存
order、order_box數據表瓶頸問題服務器
本地環境MAMP微信
yii2-debug 神器小強yii2
yii2的各類緩存app
代碼的修改最小化,儘可能不動核心代碼以防止引入bug,最後在進行數據庫和服務器的優化。yii
那咱就開始吧~
當我第一次聽客戶這樣說的時候,就已經知道慢的絕對不是登錄,這個系統沒有權限、沒有不少日誌、沒有登錄後的事件、僅僅就是一個登錄而已。
那極可能就是慢在登錄後進入的第一個頁面而給客戶的感受是登錄慢。
那就看看這個頁面
頁面邏輯很簡單,就是一個銷售圖表,天天的銷售額曲線,那麼問題最可能出如今這個圖表上,畢竟銷售額的計算看代碼都是從訂單實時分析出來的。
用yii2-debug看一看
果不其然,yii2-debug告訴我此action整個響應時間爲2.652秒,而數據庫就用了2.518秒,查詢次數39次...
看看每一個查詢,每一天執行一個SQL語句,每一個都用了90毫秒左右,畢竟是26w數據中拿數據。
彷佛問題明朗了,解決這個數據庫查詢就解決了這個頁面,先看看這個圖表的代碼實現
$y = date('Y',time()); //年 $m = date('m',time()); //月 $days_num = date('t',time()); //當月天數 $days = ''; $moneys = ''; for($i=1; $i<=$days_num; $i++){ $days .= $i.','; $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time']; $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time']; $money = Order::find() ->where(['>','dish_id',0]) ->andWhere(['pay_state'=>'pay']) ->andWhere(['>=','pay_time',$begin_time]) ->andWhere(['<=','pay_time',$end_time]) ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id]) ->sum('money'); $moneys .= ($money > 0 ? $money : 0).','; }
優化的步驟很簡單:首先看能不能減小查詢次數,若是不能就加速查詢,若是還不能就緩存結果。
在這段代碼中,不管今日是幾號,都進行了整月天數的查詢,我先來去掉不應進行的查詢。
修改及其簡單,只是增長了3行代碼
for($i=1; $i<=$days_num; $i++){ $days .= $i.','; if($i > date('d',time())){ $moneys .= "0,"; continue; } ........ }
可是經過減小沒必要要的查詢的結果是
數據庫檢索從39減小到30次,耗時從2.518秒減小到1.982秒。
對於加速查詢無外乎表類型選擇及索引的添加,本着表是全部action的表,蒼老師是世界的蒼老師,我先不動,只是記錄下dish_id、pay_state、pay_time、store_id四個字段,後面對order表進行改造的時候再考慮他們。
而後對於這種統計類數據,沒有必要每次都實時讀取,我應該加一個緩存。
若是你不會玩Yii2緩存,請移步到 Yii2緩存系列 先學習。
看這個圖表和當下代碼,個人緩存能夠按照月份來,可是還要保證當天銷量的實時性,因此我緩存的是本月今天以前的數據。
首先去配置文件 config/web.php 設置
// conf/web.php ... 'cache' => [ 'class' => 'yii\caching\FileCache', ], ...
採用默認的就好,代碼進行一點小手術(將今天以前的代碼單獨處理),核心邏輯不變。
$cache = Yii::$app->cache; $cacheKey = "month-report-{$m}"; $days = ''; $moneys = ''; // 今天以前的數據,也是咱們要緩存的數據 $monthDatBeforeToday = $cache->get($cacheKey); if ($monthDatBeforeToday === false) { for($i = 1;$i < date('d',time());$i++){ $days .= $i.','; $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time']; $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time']; $money = Order::find() ->where(['>','dish_id',0]) ->andWhere(['pay_state'=>'pay']) ->andWhere(['>=','pay_time',$begin_time]) ->andWhere(['<=','pay_time',$end_time]) ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id]) ->sum('money'); $moneys .= ($money > 0 ? $money : 0).','; } $monthDatBeforeToday = ['days'=>$days,'moneys'=>$moneys]; $cache->set($cacheKey,$monthDatBeforeToday,7200); } $days = $monthDatBeforeToday['days']; $moneys = $monthDatBeforeToday['moneys']; for($i=date('d',time());$i<=$days_num; $i++){ $days .= $i.','; if($i > date('d',time())){ $moneys .= "0,"; continue; } $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time']; $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time']; $money = Order::find() ->where(['>','dish_id',0]) ->andWhere(['pay_state'=>'pay']) ->andWhere(['>=','pay_time',$begin_time]) ->andWhere(['<=','pay_time',$end_time]) ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id]) ->sum('money'); $moneys .= ($money > 0 ? $money : 0).','; }
固然,從編寫上講,這段代碼能夠繼續優化,可是在原理上已經完成了,我將今天以前的數據都緩存下來,今天的實時讀取,這樣就知足了這個圖表和原來同樣的結果,爲防止其餘地方對訂單的修改,換成我2個小時更新一次。
用yii2-debug看看結果。
數據庫從39次到10次,耗時從2.518秒到0.1秒
Action執行從2.652秒減小到0.212秒
初步使用咱們Yii2優化三原則中的兩條,Action執行提升了12倍、數據庫耗時減小了23倍。
優化的步驟很簡單:首先看能不能減小查詢次數,若是不能就加速查詢,若是還不能就緩存結果。
記住它,一點點來,萬物都可優化。
這僅僅是對統計數據採用緩存小試牛刀,若是對於訂單列表這種檢索那就要用到第二條原則了(如何讓查詢語句更快)。
下一篇將爲你講解 對一個26萬數據的MYSQL表Yii2程序優化實戰(真實例子)之二 【開刀數據表】
(完)
本文原創發佈於微信公衆號 北哥小報 , 嚴謹的原創技術文,還有一些其餘研究。