Improvements to WP_Query performance in 6.1

Adding caching to database queries in WP_Query

WordPress 6.1 includes an improvement to how database queries are performed in the WP_Query class, resulting in database queries will be cached. This means that if the same database query is run more than once, the result will be loaded from cache. For those using persistent object caching, this will mean that the database query will not run again until caches are invalidated, resulting in far fewer queries to the database. Sites using in-memory caching will also see the benefit of not repeating these queries, though the performance improvement will not be as significant. 

For those doing custom development, please ensure that you are using coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. functions such as wp_insert_post to add posts to the database. These functions are well-maintained, and by using them, you ensure that caches are correctly invalidated. If you are updating the database directly, then it is strongly recommended that you call the clean_post_cache function after updating the database row. 

It is worth noting that by default, all calls to WP_Query will be cached going forward. It is possible to opt out of caching queries by simply passing the cache_results parameter as false. See example:

$args = array(
   'posts_per_page' => 50,
   'cache_results'  => false
);
$query = new WP_Query( $args );

It is also possible to globally disable caching, using a filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.:

function disable_caching( $wp_query ) {
   $wp_query->query_vars['cache_results'] = false;
}
add_action( 'parse_query', 'disable_caching' );

Disabling caching like this should only be done in extreme cases. For best performance, it is highly recommended to keep caching enabled and invalidate caches using the clean_post_cache function. 

Cache keys are generated using the parameters passed to the WP_Query class instance. However, the following parameters are ignored:

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

These parameters do not affect the database query that is run. The most important ignored parameter is fields. This means that if you run the following: 

$args1 = array(
	'posts_per_page' => 50,
	'fields'  => 'ids'
);
$query1 = new WP_Query( $args1 );

$args2 = array(
	'posts_per_page' => 50,
	'fields'  => 'all'
);
$query2 = new WP_Query( $args2 );

In both cases, the query will now request all fields, so that the result can be cached and then be used regardless of the fields parameter. Prior to this change, the database query for those two was different, but keeping it like that would have resulted in multiple caches for effectively subsets of the same data. This means that there is now less of a performance improvement when limiting fields ids than there was in the previous version of WordPress. 

This change also means that the update_post_meta_cache and update_post_term_cache caches are always respected. 

In cases where caching was added to WP_Query by using plugins such as advanced-post-cache, Enhanced Post Cache or Cache WP_Query, it is recommended that these plugins are disabled and removed as they are no longer required.

For more info, see TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #22176

Prime users cache in WP_Query

WordPress 6.1 introduces a new function, update_post_author_caches. Prior to 6.1, sites with multiple authors required several single database queries to get author information, as the user is loaded as part of the loopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop.. Instead of loading each user one by one, user (author) caches are now primed in a single database call by calling update_post_author_caches at the start of the loop, resulting in far fewer database queries. 

This function accepts an array of post objects and will prime user caches. Calls to update_post_author_caches have also been added in key parts of the codebase to improve database performance.

For more info, see Trac ticket #55716

Prime linked objects for menu items

A new function has been added to core called update_menu_item_cache. It accepts an array of post objects and will prime caches for post or terms referenced in a menu item. A new parameter for WP_Query has been added called update_menu_item_cache. When set to true it will call update_menu_item_cache which will allow you to prime menu items in a two database queries (one for posts and one for terms). 

For more info, see Trac ticket #55620

get_page_by_title now uses WP_Query

The function get_page_by_title now uses WP_Query. Previously,  this function used a raw database query to get the page by title. As noted above, WP_Query is now cached, meaning that calls to get_page_by_title will also be cached. This also has the benefit of running through all the filters in WP_Query

For more info, see Trac ticket #36905 

Thanks to @flixos90@milana_cap@peterwilsoncc for peer review and @mxbclang for proofreading.

#6-1, #dev-notes, #dev-notes-6-1, #performance