ThinkPHP執行長時間任務時,可能致使PHP使用內存愈來愈大,最後由於內存超出配置限額而程序掛掉。 php
其實這在好久之前就無心之中發現的一個問題。數據庫
3.x以前有這個問題,5.0之後的,應該是已修復了的(我看了下5.0.6)。數組
這個問題,一句話說明,是由於ThinkPHP在記錄系統日誌的時候出現的問題(ThinkPHP在運行過程當中,會記錄本身的運行日誌,根據是否開啓DEBUG模式,記錄的日誌信息有不一樣,很少說)。spa
由於一個進程執行完,會在不少地方打點記錄日誌,可是爲了提升寫日誌文件的效率,ThinkPHP先是給全部日誌信息記錄在內存裏(一個數組),最後一次性寫入文件。正是由於這個科學的作法,致使了可能出現的不科學的結果,那就是,在長時間執行循環任務時,尤爲是會循環操做數據庫時而又開啓了DEBUG模式的狀況下(日誌信息會記錄數據庫語句等,信息較多),就會有大量的日誌信息,愈來愈多的存入這個數組裏,形成內存佔用愈來愈大,最終可能致使程序奔潰。命令行
看代碼:debug
/**
* 文件:ThinkPHP\Library\Think\Log.class.php * 記錄日誌 而且會過濾未經設置的級別 * @static * @access public * @param string $message 日誌信息 * @param string $level 日誌級別 * @param boolean $record 是否強制記錄 * @return void */ static function record($message,$level=self::ERR,$record=false) { if($record || false !== strpos(C('LOG_LEVEL'),$level)) { self::$log[] = "{$level}: {$message}\r\n"; } }
若是遇到這種狀況,修改一下此方法便可。這裏提供一個最簡單的修改方法(在未開啓DEBUG模式的狀況下,直接不記錄日誌):調試
static function record($message,$level=self::ERR,$record=false) { if( !APP_DEBUG ) return false; //在debug爲開啓狀況下,不記錄日誌 by ztg if($record || false !== strpos(C('LOG_LEVEL'),$level)) { self::$log[] = "{$level}: {$message}\r\n"; } }
這樣,當執行長時間的循環任務時,給DEBUG關閉後,則無日誌記錄,可不會再出現這種狀況了。日誌
另外,附帶ThinkPHP5.0後的代碼,能夠看出,在保存日誌的機制上,已經徹底變了,會循環的定量保存,而後清空內容,避免這種狀況:code
/** * 記錄調試信息 * @param mixed $msg 調試信息 * @param string $type 信息類型 * @return void */ public static function record($msg, $type = 'log') { self::$log[$type][] = $msg; if (IS_CLI && count(self::$log[$type]) > 100) { // 命令行下面日誌寫入改進 self::save(); } } /** * 保存調試信息 * @return bool */ public static function save() { if (!empty(self::$log)) { if (is_null(self::$driver)) { self::init(Config::get('log')); } if (!self::check(self::$config)) { // 檢測日誌寫入權限 return false; } if (empty(self::$config['level'])) { // 獲取所有日誌 $log = self::$log; if (!App::$debug && isset($log['debug'])) { unset($log['debug']); } } else { // 記錄容許級別 $log = []; foreach (self::$config['level'] as $level) { if (isset(self::$log[$level])) { $log[$level] = self::$log[$level]; } } } $result = self::$driver->save($log); if ($result) { self::$log = []; } return $result; } return true; }