Кэширование запросов в WordPress 6.1 для WP_Query и WP_Term_Query

Кэширование запросов: WP_Query (WP 6.1) и WP_Term_Query (WP 6.4)

С версии WordPress 6.1 в ядро WordPress добавлено кэширование определённых запросов к базе данных, в частности, для WP_Query. Эта функция включена по умолчанию.

С версии WordPress 6.4 аналогичное кэширование добавлено для запросов WP_Term_Query, также включено по умолчанию.

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

Если у вас большой сайт и вы используете плагин для кэширования объектов (например, Redis Cache), это кэширование может принести больше вреда, чем пользы.

Как отключить кэширование?

Кэширование запросов к базе данных включено по умолчанию в WP_Query и WP_Term_Query.

Полное отключение кэширования

Если вам нужно глобально отключить кэширование для всех запросов, используйте фильтры parse_query и pre_get_terms:

// Отключение кэширования WP_Query: по умолчанию WP кэширует результаты WP_Query с 6.1
add_action( 'pre_get_posts', 'disable_get_posts_caching' );
function disable_get_posts_caching( $wp_query ) {
    $wp_query->query_vars['cache_results'] = false;
}

// Отключение кэширования WP_Term_Query.
add_action( 'pre_get_terms', 'disable_get_terms_caching' );
function disable_get_terms_caching( $wp_term_query ) {
    $wp_term_query->query_vars['cache_results'] = false;
}

Отключать кэширование для терминов таким образом не рекомендуется, так как это может снизить производительность. Вместо этого рассмотрите возможность установки ограниченного времени хранения кэша (подробнее ниже).

Кэширование терминов существовало всегда. С версии WP 6.2 оно было перенесено в отдельную группу кэша term-queries, а в 6.4 был добавлен параметр cache_results, позволяющий отключать кэширование.

Отключение кэширования для конкретных запросов

Укажите параметр cache_results=false:

$query = new WP_Query( [
    'posts_per_page' => 50,
    'cache_results'  => false,
] );

$terms = get_terms( [
    'taxonomy'      => 'post_tag',
    'hide_empty'    => false,
    'cache_results' => false,
] );

Установка более короткого времени жизни (TTL)

По умолчанию записи в кэше не имеют срока годности и могут расти бесконечно.

Вместо полного отключения кэширования вы можете уменьшить размер кэша, установив максимальное время хранения (TTL) для таких записей.

Все плагины для постоянного кэширования должны иметь соответствующий хук.

Рассмотрим плагин redis-cache как пример. У него есть хук, подходящий для наших нужд, называющийся redis_cache_expiration:

/**
 * Фильтрует время истечения кэша
 *
 * @since 1.4.2
 * @param int    $expiration Время в секундах, через которое запись истекает. 0 — без истечения.
 * @param string $key        Ключ кэша.
 * @param string $group      Группа кэша.
 * @param mixed  $orig_exp   Исходное значение истечения до валидации.
 */
$expiration = apply_filters( 'redis_cache_expiration', $expiration, $key, $group, $orig_exp );

Группы кэша, которые нам нужны: term-queries и post-queries:

// Более короткое время истечения кэша (TTL) для некоторых групп кэша.
add_filter( 'redis_cache_expiration', 'set_post_term_queries_cache_ttl', 10, 3 );
function set_post_term_queries_cache_ttl( $ttl, $key, $group ) {
    static $short_ttl_groups = [
        'term-queries',
        'post-queries',
        'site-queries',
        'comment-queries',
    ];

    if( in_array( $group, $short_ttl_groups, true ) ){
        return HOUR_IN_SECONDS * 6;
    }

    return $ttl;
}

Исключение по ключу:

// Более короткое время истечения кэша (TTL) для некоторых групп.
add_filter( 'redis_cache_expiration', 'set_post_term_queries_cache_ttl', 10, 3 );
function set_post_term_queries_cache_ttl( $ttl, $key, $group ) {
    if( preg_match( '/get_terms:|wp_query:|get_users:/', $key ) ){
        return HOUR_IN_SECONDS * 6;
    }

    return $ttl;
}

Вы можете увидеть, какие ключи используются в коде:

  • WP_Query::generate_cache_key()
  • WP_Term_Query::generate_cache_key()
  • WP_User_Query::generate_cache_key()

Чтобы проверить, всё ли работает корректно: очистите кэш, подключитесь к Redis и проверьте TTL одной записи.

Пример:

/data # redis-cli
127.0.0.1:6379>
127.0.0.1:6379> KEYS '*get_terms*'
1) "hl_1:term-queries:get_terms-bfa6333d60e0407f94a7fb93d5c9af0a-0.69511300 1740324474"
127.0.0.1:6379> TTL 'hl_1:term-queries:get_terms-bfa6333d60e0407f94a7fb93d5c9af0a-0.69511300 1740324474'
(integer) 21573
127.0.0.1:6379>

Здесь мы видим TTL равный 21573 (секунд) — это 21573 ÷ 3600 = 5.99 часов.

Redis отслеживает время жизни (TTL) каждого ключа отдельно.

Когда ключ хранится с TTL, Redis:

  • Периодически проверяет ключи и автоматически удаляет устаревшие.
  • Если ключ не истёк, он остаётся в памяти и доступен.
  • Вернёт пустое значение при попытке получить ключ с истёкшим TTL.

Redis не удаляет ключи точно в момент истечения; они постепенно удаляются в фоновом режиме, чтобы минимизировать нагрузку.

Кэширование запросов в WP_Query

С версией WordPress 6.1 кэшируются запросы к базе данных, реализованные в классе WP_Query. Поэтому, если один и тот же запрос к базе данных выполняется несколько раз, последующие результаты будут загружены из кэша.

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

Если постоянное кэширование не настроено (используется кэширование в памяти), сайт также получит выгоду, так как повторные запросы на странице не будут выполняться, а будут взяты из кэша. Однако улучшение производительности в этом случае будет не таким значительным.

Для разработчиков

Разработчикам нужно убедиться, что встроенные функции WP, такие как wp_insert_post(), используются для добавления или обновления записей. Эти функции обеспечивают правильную инвалидизацию кэша.

Если вы обновляете базу данных напрямую, после обновления строки в базе данных обязательно очистите кэш поста, используя функцию clean_post_cache().

Ключ кэша (как определяется, существует ли кэш)

Существует ли кэш или нет, определяется сравнением ключа кэша.

Ключ кэша создаётся из параметров запроса, переданных в WP_Query. Однако следующие параметры игнорируются:

  • suppress_filters
  • cache_results
  • fields
  • update_post_meta_cache
  • update_post_term_cache
  • update_menu_item_cache
  • lazy_load_term_meta

Эти параметры не влияют на запрос к базе данных. Наиболее важный параметр — fields. Это означает, что если вы выполните следующее:

$query1 = new WP_Query( [
    'posts_per_page' => 50,
    'fields' => 'ids'
] );

$query2 = new WP_Query( [
    'posts_per_page' => 50,
    'fields' => 'all'
] );

В обоих случаях запрос теперь будет запрашивать все поля, чтобы результат можно было кэшировать и использовать независимо от параметра полей.

Ранее запрос к базе данных для этих двух случаев был разным, но поддерживать это поведение означало бы создавать несколько кэшей для на самом деле подмножеств одних и тех же данных. Это означает, что теперь улучшение производительности при ограничении полей до ids будет менее заметным, чем в предыдущей версии WordPress.

Это изменение также означает, что параметры (caches) update_post_meta_cache и update_post_term_cache всегда учитываются.

Теперь ненужные плагины

Рекомендуется отключить и удалить плагины, которые добавляют функциональность кэширования в WP_Query. Например:

  • advanced-post-cache
  • Enhanced Post Cache
  • Cache WP_Query

Кэширование пользователей в WP_Query

В WordPress 6.1 была добавлена новая функция update_post_author_caches().

До версии 6.1 на сайтах с несколькими авторами требовалось несколько отдельных запросов к базе данных для получения информации об авторах. Это происходило, потому что данные пользователей загружались во время цикла.

Теперь вместо загрузки каждого пользователя отдельно данные для всех пользователей, присутствующих в цикле, загружаются за один запрос в начале цикла — смотрите update_post_author_caches(). Это значительно снижает количество запросов к базе данных.

Вызовы update_post_author_caches() были добавлены также в других ключевых местах основного кода. Это повысило производительность.

Кэширование связанных объектов для элементов меню

В ядро добавлена новая функция update_menu_item_cache(), которая принимает массив объектов постов и кэширует объекты постов или терминов, на которые ссылаются элементы меню.

Для WP_Query добавлен новый параметр update_menu_item_cache. Если он установлен в true, то будет вызвана update_menu_item_cache(), что позволит вам кэшировать элементы меню в два запроса к базе данных (один для постов и один для терминов).

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *