尽pipe增加了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 

该错误消息似乎支持memory_limit = 256M的php.ini设置被认为是真实的PHP。 不过,我已经在wordpress中使用了几个内存监控插件,他们都报告说,该网站在稳定状态下使用〜35MB的内存,直到OOME发生之前,它似乎永远不会增长。 记忆先前已被设定在较低的水平,并反复增加而不修复症状。 几乎总是4分钟。 有时候恰好是3分钟或者3分30秒等。我安装了一个wordpress cron插件,看看是否有计划在4分钟的时间内运行一些东西,但是没有任何东西出现。

我已经检查了httpd.conf文件并确认没有RLimitMEM设置。 我也通过apachectl -V确认我正在查看正确的httpd.conf文件。 顶部说有半个GB的空闲RAM可供系统使用。 我发现访问日志中的条目和php错误日志中的OOME之间没有关联。

这台服务器托pipe了相当多的网站。 我不pipe理服务器,但我一直在帮助解决相关网站上的一些问题。

对于如何继续解决这个问题,我将不胜感激。

我终于明白了这一点。

问题是在网站上安装的两个插件(特别是两个插件的configuration方式)之间有冲突.iThemes安全插件( http://ithemes.com/security )被configuration为定期进行站点备份。 使数据库备份的代码执行数据库中每个表的转储,并假定每个表的内容将完整地放入内存中。 与此无关,网站上还安装了另一个名为Redirection( http://urbangiraffe.com/plugins/redirection/ )的插件,用于维护redirect。 这个插件有configuration选项来loggingredirect以及404响应。 不幸的是,这些日志被设置为永不过期,而指向我们网站的僵尸networkingstream量的B / C已经累计了近90,000个redirect日志和30,000个404日志。 由于iThemes Security插件在尝试创build备份时尝试将整个表加载到内存中,因此wp_redirection_logs表占用了来自php的所有可用内存,并使进程崩溃。 我怀疑iThemes Security试图在每个可用的机会重新运行失败的备份,每3-4分钟造成一次错误。

我通过将redirect日志设置更改为在5天后过期redirect条目并且根本不logging404错误来解决此问题。 然后,我不得不反复刷新日志页面,以获得过期的条目被删除。 内存不足错误不再发生。

我从此听说过其他WordPress用户在iThemse安全插件类似的错误,在各种wordpress表上做SELECT *。 以下是我如何debugging这个问题的情况下的解释,如果你是相似的,但不完全一样:

在正常修复(增加php内存并确保实际工作,确认我的内存使用情况稳定并且随着时间的推移低)之后,我开始对错误进行故障排除的方法是将一些debugging日志语句添加到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行添加的代码; 第一个将$ emitvariables赋值为false来跟踪我们是否已经logging了这个请求,然后是5条if语句来实际logging。

在我们开始将查询结果读入内存之前,打印php所消耗的当前内存和峰值内存,并打印出执行的查询。 在我们开始阅读结果之前,记忆会告诉你是否有合理的记忆。 如果可用内存接近您的限制(在几MB内),那么问题可能在其他地方,并且您没有足够的空间来运行合理大小的查询。 如果像我一样,在运行查询之前你有大量的空闲内存,那么看看在内存用完之前运行的查询是什么(我的日志条目都是在php错误日志中,但是如果你的是分割的跨越和iThemes日志和php错误日志,基于时间戳两者之间的相关性。)内存错误之前立即运行的查询是一个让你起来的查询。

在我的情况下,它是一个SELECT * FROM wp_redirection_logs ;. 该表已经失去了控制b / credirect插件在我的网站上configuration错误,永不过期的日志条目。 通过阅读iThemes安全插件的代码,很明显,备份操作对数据库中每个以wp_前缀开头的表执行SELECT * FROM查询(或者其他前缀,如果您的站点被configuration为使用不同的前缀,网站或其他东西。)iThemes安全的其他领域(如404错误日志)似乎也发出SELECT *查询可能超过可用内存的表。 一旦你发现查询是什么,你可以开始推断你的错误的原因,也许修剪不必要的数据库内容来解决像我这样的问题。

如果您通过这些步骤并回报,我很乐意提供build议。