Почти каждые 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, и все они сообщают, что сайт использует ~ 35 МБ ОЗУ в стабильном состоянии, и, похоже, он вообще не растет, пока не произойдет OOME. Память ранее была установлена на более низком уровне и многократно увеличивалась без устранения симптома. Почти всегда это ровно 4 минуты. Иногда это ровно 3 минуты или 3 минуты 30 секунд и т. Д. Я установил плагин cron для wordpress, чтобы проверить, запланировано ли выполнение чего-либо с интервалом в 4 минуты, но, похоже, ничего не вышло.
Я проверил файл httpd.conf и подтвердил, что параметр RLimitMEM отсутствует. Я также подтвердил через apachectl -V, что смотрю на правильный файл httpd.conf. Топ говорит, что системе доступно половину ГБ свободной оперативной памяти. Я не нашел корреляции между записями в журнале доступа и OOME в журнале ошибок php.
На этом сервере размещено довольно большое количество сайтов. Я не управляю сервером, но я помогал устранять некоторые проблемы на данном сайте.
Буду признателен за любые предложения, как продолжить устранение неполадок.
Я наконец понял это.
Проблема заключалась в конфликте между двумя плагинами, установленными на сайте (и, в частности, в способе настройки этих двух плагинов). Плагин iThemes Security (http://ithemes.com/security) настроен на периодическое создание резервных копий сайта. Код для создания резервной копии БД делает дамп каждой таблицы в базе данных и предполагает, что содержимое каждой таблицы полностью умещается в памяти. Не связанный с этим, на сайте установлен еще один плагин под названием Redirection (http://urbangiraffe.com/plugins/redirection/), который используется для поддержки переадресации. Этот плагин имеет возможность конфигурации для регистрации перенаправлений, а также 404 ответов. К сожалению, эти журналы были настроены на неограниченный срок действия, и в связи с объемом трафика ботнета, направленного на наш сайт, накопилось почти 90 000 журналов перенаправления и 30 000 журналов 404. Поскольку плагин iThemes Security пытается загрузить всю таблицу в память, когда он пытается создать резервную копию, таблица wp_redirection_logs потребляет всю доступную память из php и приводит к сбою процесса. Я подозреваю, что iThemes Security пыталась повторно запустить неудачное резервное копирование при каждой доступной возможности, что приводило к ошибкам каждые 3-4 минуты.
Я исправил это, изменив настройку журнала перенаправления, чтобы срок действия записей перенаправления истекал через 5 дней, а не регистрировать ошибки 404 вообще. Затем мне пришлось несколько раз обновлять страницу журнала, чтобы удалить просроченные записи. Ошибка нехватки памяти больше не возникает.
[править] С тех пор я слышал от других людей, работающих с Wordpress, которые сталкивались с похожими ошибками, когда плагин iThemse Security выполнял SELECT * для различных таблиц WordPress. Ниже приводится объяснение того, как я отлаживал эту проблему, если ваша проблема похожа, но не совсем такая же:
Способ, которым я решил устранить ошибку после обычных исправлений (увеличение памяти 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, прежде чем мы начнем считывать результаты запроса в память, а также распечатывает запрос, который был выполнен. Память даст вам представление о том, достаточно ли у вас доступной памяти, прежде чем мы начнем считывать результаты. Если доступная память близка к вашему пределу (в пределах нескольких МБ), то проблема, вероятно, в другом месте, и у вас действительно нет свободного места для выполнения запроса разумного размера. Если, как и я, у вас есть тонны свободной памяти до запуска запроса, посмотрите, какой запрос выполняется прямо перед тем, как у вас закончится память (все мои записи журнала были в журнале ошибок php, но если ваши разделены Через и журнал iThemes и журнал ошибок php коррелируют между ними на основе временных меток.) Запрос, который был запущен непосредственно перед ошибкой памяти, - это тот, который вас взорвал.
В моем случае это был SELECT * FROM wp_redirection_logs ;. Эта таблица вышла из-под контроля, потому что плагин перенаправления был неправильно настроен на моем сайте, чтобы никогда не истекать записи журнала. Из чтения кода плагина безопасности iThemes становится ясно, что действие резервного копирования выполняет запрос SELECT * FROM для каждой таблицы в вашей БД, которая начинается с префикса wp_ (или другого префикса, если ваш сайт настроен на использование другого префикса из-за нескольких site или что-то еще.) Другие области iThemes Security (например, журналы ошибок 404), похоже, также создают запросы SELECT * к таблицам, которые могут превышать доступную память. Как только вы найдете, что это за запрос, вы можете начать рассуждать о причине вашей ошибки и, возможно, удалить ненужное содержимое БД, чтобы обойти проблему, как это сделал я.
Я был бы рад предложить предложения, если вы выполните эти шаги и сообщите об этом.