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

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

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

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

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

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

Кэширование запросов к БД включено по умолчанию в WP_Query и WP_Term_Query.

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

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

/// Disable WP_Query caching: by default WP cache WP_Query result since WP 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;
}

/// Disable WP_Term_Query caching.
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:

/**
 * Filters the cache expiration time
 *
 * @since 1.4.2
 * @param int    $expiration The time in seconds the entry expires. 0 for no expiry.
 * @param string $key        The cache key.
 * @param string $group      The cache group.
 * @param mixed  $orig_exp   The original expiration value before validation.
 */
$expiration = apply_filters( 'redis_cache_expiration', $expiration, $key, $group, $orig_exp );

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

/// Shorter cache_expiration time (TTL) for some groups of cache.
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;
}

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

/// Shorter cache_expiration time (TTL) for some groups of cache.
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;
}

Какой ключ используется можно посмотреть в коде:

Чтобы проверить что все работает правильно, нужно: очисить кэш; подключиться к редис и посмотерть TTL отдельной записи.

Например:

/data # redis-cli
127.0.0.1:6379>
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>
127.0.0.1:6379>
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 истёк.
    Если ключ ещё не истёк, он просто остаётся в памяти и доступен как обычно.
  • При попытке получить ключ с истёкшим TTL Redis вернёт, будто ключа не существует.

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

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

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

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

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

Разработчикам

Разработчикам нужно убедиться, что для добавления или обновления постов используются встроенные функции 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'
] );

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

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

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

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

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

Дополнительная информация см. в Trac-запросе #22176.

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

В WordPress 6.1 появилась новая функция, update_post_author_caches().

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

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

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

Дополнительная информация см. в Trac-запросе #55716

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

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

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

Дополнительная информация см. в Trac-запросе #55620

--

Источник