問題描述:
有一個指令碼是用來確認收貨的。
像淘寶一樣,我們買的商品,快遞送完貨以後,可以手動點選已收貨,或者是過幾天自動改狀態為已收貨。
就做這個功能的指令碼執行時間很長,是一開始挺快的,越來越慢。
解決過程:
開始的時候感覺效能有問題,把一次性取出所有資料改成一次取出100條
下手太快,感覺沒找到問題原因。
用什麼工具解決這個問題呢?
常用的xhprof,我本地是裝了這個擴充套件的,
問題是本地環境沒有這麼多資料,我把線上資料匯入本地環境(線上資料大,費時費力),執行了下,沒發現這個問題。
我想到線上環境和本地環境不一樣,還是要到線上環境除錯。
問題是線上沒有許可權修改程式碼,再說了修改程式碼容易出錯,使用者訪問時候會報錯。
線上環境沒有xhprof,用什麼工具好呢?
經常聽人說使用用strace調查一些問題,我也試試看
strace -p pid
看輸出看不懂
我把問題和我們的架構師說了一遍,給他看strace的輸出,問他看懂嗎?有什麼辦法解決這個問題。
他很果斷,說是看程式碼,不用其他工具。
我從頭看程式碼,呼叫的來來回回,暈暈乎乎。
後來想起來把線上程式碼拷到我的賬號的家目錄,可以除錯,接下來順利多了
我線上發現了有一個方法,執行一段時間需要2s甚至更長時間
strace列印的日誌中看到了:
sendto(6, "*4\r\n$7\r\nHINCRBY\r\n$23\r\norder_stat"..., 77, MSG_DONTWAIT, NULL, 0) = 77
sendto(6, "*1\r\n$4\r\nEXEC\r\n", 14, MSG_DONTWAIT, NULL, 0) = 14
sendto(6, "*2\r\n$6\r\nEXISTS\r\n$23\r\norder_stati"..., 46, MSG_DONTWAIT, NULL, 0) = 46
關鍵字HINCRBY EXEC EXISTS,像是redis操作
主要是程式一直沒有卡住過,到了後來一直就是這些操作
我透過下面命令檢視了lsof -d 6
結果是:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
php 25106 6u IPv4 3612956560 0t0 TCP *.*.*.*:25571->*.*.*.*:6379 (ESTABLISHED)
6379埠是我熟悉的redis埠
問了同事,什麼地方有redis加一操作,他告訴了我程式碼的位置,我一看知道了就是這兒的問題
為什麼一開始沒有那麼多的自增加一操作,執行到後來卻有了呢?
我看到程式碼中有用到事件模型,猜測是這兒的原因,果不其然:
迴圈中呼叫了下面程式碼:
Helper::statistics($this, 'orderFinish', [
'user_name' => !empty($order_info['member_name']) ? $order_info['member_name'] : '',
'user_id' => !empty($order_info['member_id']) ? $order_info['member_id'] : 0
]);
該方法定義是這樣:
$statistic = new OrderStatistics();
$obj->on($action, [$statistic, $action], $data);
$obj->trigger($action);
可以看到監聽的事件越來越多,所以就是我們看到的現象,執行到後來越來越慢
問題總結:
事件模型使用不當,迴圈中,給一個物件不斷新增事件。而我們想要的效果是迴圈一次,事件要定義一次,觸發一次,銷燬一次。
解決辦法:
我的解決方案,程式碼改成這樣:
$statistic = new OrderStatistics();
$event = new Event();
$event->data = $data;
$statistic->{$action}($event);
我感覺在這個地方應用事件模型,有點牽強。
另外一個解決方案,
$statistic = new OrderStatistics();
$obj->on($action, [$statistic, $action], $data);
$obj->trigger($action);
$obj->off($action, [$statistic, $action]);//加了這一行,呼叫一次,立馬把事件取消掉
大家喜歡哪種解決方案?
參考資料:
https://www.jianshu.com/p/8a247cae629a
本作品採用《CC 協議》,轉載必須註明作者和本文連結