儘管增加了 PHP 記憶體限制並確認沒有 RLimitMEM,但 Wordpress 網站重複出現 PHP 記憶體不足錯誤
幾乎每 4 分鐘,我就會在 php 錯誤日誌中看到以下記憶體不足錯誤:
01-Jul-2014 21:50:03 UTC] PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 72 bytes) in /home/[sitename]/public_html/wp-includes /wp-db.php on line 1938
錯誤消息似乎支持 php.ini 的 memory_limit = 256M 設置被 PHP 認為是正確的。但是,我在 wordpress 中使用了幾個記憶體監控外掛,它們都報告說該站點在穩定狀態下使用了大約 35MB 的 RAM,並且在 OOME 發生之前它似乎根本沒有增長。記憶以前被設置在較低的水平,並在沒有解決症狀的情況下反復增加。幾乎總是正好 4 分鐘。有時正好是 3 分鐘或 3 分鐘 30 秒等。我安裝了一個 wordpress cron 外掛,以查看是否有計劃以 4 分鐘的間隔執行,但似乎沒有。
我檢查了 httpd.conf 文件並確認沒有 RLimitMEM 設置。我還通過 apachectl -V 確認我正在查看正確的 httpd.conf 文件。Top 說系統有半 GB 的可用 RAM。我發現訪問日誌中的條目與 php 錯誤日誌中的 OOME 之間沒有關聯。
該伺服器託管著相當多的站點。我不管理伺服器,但我一直在幫助解決相關站點上的一些問題。
對於如何繼續解決此問題的任何建議,我將不勝感激。
我終於想通了。
問題是站點上安裝的兩個外掛之間的衝突(特別是兩個外掛的配置方式)。 iThemes 安全外掛 ( http://ithemes.com/security ) 被配置為定期進行站點備份。進行數據庫備份的程式碼會轉儲數據庫中的每個表,並假設每個表的內容都可以完全放入記憶體中。與此無關,網站上安裝了另一個外掛,名為 Redirection ( http://urbangiraffe.com/plugins/redirection/) 用於維護重定向。該外掛具有記錄重定向和 404 響應的配置選項。不幸的是,這些日誌被設置為永不過期,並且針對我們網站的殭屍網路流量的 b/c 累積了近 90,000 條重定向日誌和 30,000 404 條日誌。由於 iThemes Security 外掛在嘗試創建備份時嘗試將整個表載入到記憶體中,因此 wp_redirection_logs 表消耗了 php 中的所有可用記憶體並導致程序崩潰。我懷疑 iThemes Security 試圖在每個可用的機會重新執行失敗的備份,導致每 3-4 分鐘出現一次錯誤。
我通過將重定向日誌設置更改為在 5 天后使重定向條目過期並且根本不記錄 404 錯誤來解決此問題。然後我不得不反复刷新日誌頁面以刪除過期條目。不再發生記憶體不足錯誤。
$$ edit $$從那以後,我從其他 wordpress 人那裡聽說過 iThemse Security 外掛在各種 wordpress 表上執行 SELECT * 時遇到了類似的錯誤。以下是我如何調試此問題的說明,以防您的問題相似但不完全相同: 在正常修復(增加 php 記憶體並確保實際工作,確認我的記憶體使用量隨著時間的推移穩定且低)之後,我對錯誤進行故障排除的方法是向 wp-db.php 添加一些調試日誌語句,所以我可以看到發生錯誤時發生了什麼。這是我為幫助縮小問題範圍所做的程式碼更改(請務必在嘗試此操作之前備份 wp-db.php,以便您可以輕鬆恢復設置,以防在編輯文件時出現問題):
function get_results( $query = null, $output = OBJECT ) { $this->func_call = "\$db->get_results(\"$query\", $output)"; if ( $query ) $this->query( $query ); else return null; $new_array = array(); if ( $output == OBJECT ) { // Return an integer-keyed array of row objects return $this->last_result; } elseif ( $output == OBJECT_K ) { // Return an array of row objects with keys from column 1 // (Duplicates are discarded) foreach ( $this->last_result as $row ) { $var_by_ref = get_object_vars( $row ); $key = array_shift( $var_by_ref ); if ( ! isset( $new_array[ $key ] ) ) $new_array[ $key ] = $row; } return $new_array; } elseif ( $output == ARRAY_A || $output == ARRAY_N ) { // Return an integer-keyed array of... if ( $this->last_result ) { $emited = false; foreach( (array) $this->last_result as $row ) { if ( $output == ARRAY_N ) { // ...integer-keyed row arrays if (!$emitted) { error_log("Current Mem: " . memory_get_usage() . ", eak mem: " . memory_get_peak_usage()); error_log($query); $emitted = true; } $new_array[] = array_values( get_object_vars( $row ) ); } else { // ...column name-keyed row arrays $new_array[] = get_object_vars( $row ); } } } return $new_array; } elseif ( strtoupper( $output ) === OBJECT ) { // Back compat for OBJECT being previously case insensitive. return $this->last_result; } return null; }
這是 6 行添加的程式碼;第一個將 $emitted 變數分配給 false 以跟踪我們是否已經記錄了此請求,然後將 5 行 if 子句實際記錄。
它的作用是在我們開始將查詢結果讀入記憶體之前列印出 php 消耗的目前記憶體和峰值記憶體,並列印出已執行的查詢。在我們開始閱讀結果之前,記憶體將使您了解您是否有合理的可用記憶體。如果可用記憶體接近您的限制(在幾 MB 以內),那麼問題可能出在其他地方,並且您實際上沒有足夠的空間來執行合理大小的查詢。如果像我一樣,在執行查詢之前您有大量可用記憶體,那麼請查看在記憶體不足之前執行的查詢是什麼(我的日誌條目都在 php 錯誤日誌中,但如果您的日誌被拆分跨和 iThemes 日誌和 php 錯誤日誌,根據時間戳在兩者之間關聯。
就我而言,這是一個 SELECT * FROM wp_redirection_logs;。該表已經失控 b/c 重定向外掛在我的站點上被錯誤配置為永不過期日誌條目。通過閱讀 iThemes 安全外掛的程式碼,很明顯,備份操作對數據庫中以 wp_ 前綴開頭的每個表執行 SELECT * FROM 查詢(如果您的站點配置為使用不同的前綴,則為其他前綴)站點或其他東西。)iThemes 安全性的其他區域(如 404 錯誤日誌)似乎也對可能超出可用記憶體的表發出 SELECT * 查詢。一旦你找到查詢是什麼,你就可以開始推理你的錯誤的原因,也許像我一樣修剪不必要的數據庫內容來解決這個問題。
如果您完成這些步驟並進行報告,我很樂意提供建議。