Вопрос или проблема
У меня 5000 обычных записей, 6000 записей в одном типе записей и 2000 записей в другом типе записей. Напоминаю, что это сделало таблицу wp_posts
довольно большой. Не говоря уже о том, что у меня настроены кастомные таксономии, чтобы имитировать типы записей, так что для каждой Компании A, введенной в тип записей Company, есть Компания A, которую я вручную ввел в таксономию Company. Таким образом, когда я делаю обычную запись о Компании A, я могу эффективно пометить Компанию A в записи, и эта запись появится на странице кастомного типа записи Компании A.
Спецификации : Работает WP Super Cache с Cloudflare в качестве CDN. Тема — WordPress TwentyEleven, но сильно настроенная. Сервер: 2048 RAM, 80gb Raid, 8+CPU 4x приоритет, 5000GB трафика. Трафик составляет 750k просмотров в месяц и 200k уникальных.
Я начинаю замечать, что проблемы с производительностью увеличиваются довольно быстро, с двумя случаями на прошлой неделе, когда я получил
ошибку подключения к базе данных
сообщение на сайте и мне пришлось перезапустить службу mysql. Не говоря уже о высоких средненагрузочных письмах несколько раз в день.
Запустил Debug запросы. Запустил их с WP Super Cache. Удалил WP Super Cache и попробовал W3 Total Cache с большинством включенных опций. В обоих испытаниях я увидел самое долгое время загрузки, вызванное этими двумя запросами (но только при первой загрузке! Практически 0 при последующих загрузках):
Время: 7.79628753662E-5
Запрос: SELECT FOUND_ROWS()
Вызвано из: require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/child-theme/index.php'), WP_Query->__construct, WP_Query->query, WP_Query->get_posts, WP_Query->set_found_posts, W3_Db->query, W3_DbCache->query, W3_DbCallUnderlying->query, W3_Db->query, W3_DbProcessor->query, W3_Db->default_query
Время: 6.29425048828E-5
Запрос: SET SESSION query_cache_type = 0;
Вызвано из: require('wp-blog-header.php'), require_once('wp-includes/template-loader.php'), include('/themes/child-theme/index.php'), get_footer, locate_template, load_template, require_once('/themes/child-theme/footer.php'), wp_footer, do_action('wp_footer'), call_user_func_array, Debug_Queries->the_queries, Debug_Queries->get_queries, W3_Db->query, W3_DbCache->query, W3_DbCallUnderlying->query, W3_Db->query, W3_DbProcessor->query, W3_Db->default_query
Я удалил массив, который позволял загружать типы записей в дополнение к обычным записям, но это, похоже, не дало результата.
Полный вывод примера запроса: http://pastebin.com/L0mSXe9q
Кроме того, вот код шаблона для основного индекса:
<?php if ( have_posts() ) : ?>
<?php /* Начало цикла */ ?>
<?php while ( have_posts() ) : the_post(); ?>
<?php if ( 'status' == get_post_format() ) {
get_template_part( 'content-status', get_post_format() );
} else get_template_part( 'excerpt', get_post_format() );
?>
<?php endwhile; ?>
Мне действительно неясно, как действовать дальше.
Это не должно ломать постраничное разделение:
add_filter('pre_get_posts', 'optimized_get_posts', 100);
function optimized_get_posts() {
global $wp_query, $wpdb;
$wp_query->query_vars['no_found_rows'] = 1;
$wp_query->found_posts = $wpdb->get_var( "SELECT COUNT(*) FROM wp_posts WHERE 1=1 AND wp_posts.post_type="post" AND (wp_posts.post_status="publish" OR wp_posts.post_status="private")" );
$wp_query->found_posts = apply_filters_ref_array( 'found_posts', array( $wp_query->found_posts, &$wp_query ) );
if($wp_query->query_vars['posts_per_page'] <= 0) {
$wp_query->max_num_pages = 0;
} else {
$wp_query->max_num_pages = ceil($wp_query->found_posts / $wp_query->query_vars['posts_per_page']);
}
return $wp_query;
}
Просто вставьте это в ваш functions.php, и у вас все будет в порядке.
add_filter( 'pre_get_posts', 'optimized_get_posts', 100 );
function optimized_get_posts() {
global $wp_query;
$wp_query->query_vars['no_found_rows'] = 1;
return $wp_query;
}
Ответ или решение
Для оптимизации запроса SELECT FOUND_ROWS()
в WordPress с учетом вашей структуры данных и текущих условий загрузки сервера, следует рассмотреть несколько стратегий. Давайте подробно разберем ситуацию и предложим подходы, которые помогут снизить нагрузку на базу данных и улучшить производительность вашего сайта.
Анализ ситуации
У вас довольно большая база данных с несколькими типами постов и специальными таксономиями, что, безусловно, создает нагрузку на запросы к базе данных. Учитывая, что вы видите частые высокие значения загрузки и ошибки подключения к базе данных, важно оптимизировать запросы, чтобы снизить потребление ресурсов.
Оптимизация запроса SELECT FOUND_ROWS()
Главным образом, SELECT FOUND_ROWS()
используется для получения количества постов, соответствующих последнему запросу с использованием LIMIT
. Это может вызывать значительные накладные расходы, особенно если запросы выполняются часто и таблицы большие. Вот несколько стратегий для оптимизации:
-
Отключение подсчета найденных постов:
Если вам не нужно использовать пагинацию на страницах с постами, вы можете отключить подсчет найденных постов, добавив следующий код в файлfunctions.php
вашей темы:add_filter('pre_get_posts', function($query) { if (!is_admin() && $query->is_main_query()) { $query->get_posts(); // чтобы получить ранее загруженные посты. $query->set('no_found_rows', true); // отключаем подсчет найденных постов } return $query; });
Этот код минимизирует количество запросов к базе данных, устраняя необходимость в лишнем запросе
SELECT FOUND_ROWS()
. -
Оптимизация SQL-запросов:
Если необходимо использовать пагинацию, рекомендуется вручную подсчитывать количество постов, используя менее затратный запрос. Вот пример:add_filter('pre_get_posts', function($query) { global $wpdb; if (!is_admin() && $query->is_main_query()) { // Отключаем подсчет найденных постов для основного запроса $query->set('no_found_rows', true); // Если вам нужно получить найденные посты $total_posts = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'post' AND (post_status = 'publish' OR post_status = 'private')"); $query->found_posts = $total_posts; // устанавливаем найденные посты // Устанавливаем максимальное количество страниц if ($query->get('posts_per_page') > 0) { $query->max_num_pages = ceil($total_posts / $query->get('posts_per_page')); } } return $query; });
-
Индексирование таблиц:
Убедитесь, что таблицаwp_posts
и ваши таксономии индексированы. Вы можете использоватьEXPLAIN
для анализа ваших SQL-запросов и удостовериться, что они используют индексы оптимально. -
Кэширование:
Рассмотрите возможность использования механизмов кэширования базы данных, таких как Redis или Memcached. Это может значительно снизить нагрузку на MySQL, особенно при высоком трафике.
Мониторинг и следующий шаг
После внесения изменений важно мониторить производительность сайта и запросы к базе данных с помощью инструментов, таких как Query Monitor или New Relic. Это поможет вам понять, какие запросы по-прежнему создают проблемы, и что можно улучшить.
Заключение
Согласно вашему описанию, внедрение указанных оптимизационных практик должно значительно снизить нагрузку на базу данных и повысить общую производительность сайта. Следуя предложенным стратегиям, вы сможете лучше справляться с растущими объемами трафика и эффективнее управлять ресурсами серверов.