Как оптимизировать запрос ‘select found_rows()’? Несколько предупреждений о ‘высокой нагрузке’ ежедневно.

Вопрос или проблема

У меня 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. Это может вызывать значительные накладные расходы, особенно если запросы выполняются часто и таблицы большие. Вот несколько стратегий для оптимизации:

  1. Отключение подсчета найденных постов:
    Если вам не нужно использовать пагинацию на страницах с постами, вы можете отключить подсчет найденных постов, добавив следующий код в файл 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().

  2. Оптимизация 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;
    });
  3. Индексирование таблиц:
    Убедитесь, что таблица wp_posts и ваши таксономии индексированы. Вы можете использовать EXPLAIN для анализа ваших SQL-запросов и удостовериться, что они используют индексы оптимально.

  4. Кэширование:
    Рассмотрите возможность использования механизмов кэширования базы данных, таких как Redis или Memcached. Это может значительно снизить нагрузку на MySQL, особенно при высоком трафике.

Мониторинг и следующий шаг

После внесения изменений важно мониторить производительность сайта и запросы к базе данных с помощью инструментов, таких как Query Monitor или New Relic. Это поможет вам понять, какие запросы по-прежнему создают проблемы, и что можно улучшить.

Заключение

Согласно вашему описанию, внедрение указанных оптимизационных практик должно значительно снизить нагрузку на базу данных и повысить общую производительность сайта. Следуя предложенным стратегиям, вы сможете лучше справляться с растущими объемами трафика и эффективнее управлять ресурсами серверов.

Оцените материал
Добавить комментарий

Капча загружается...