WordPress 6.1 将提高 WP_Query 查询性能

WP_Query 是 WordPress 中最重要的 class,几乎每个页面都是用它来获取文章,但是它最大的问题是,对文章进行查询的时候是直接到数据库查询的,结果没有被缓存起来,所以真正实现站点的 0SQL 就是这里。

WordPress 6.1 改进了 WP_Query 类中数据库查询的执行方式,实现 SQL 查询缓存,这意味着如果多次运行同一条 SQL 查询,查询结果将从缓存中加载。

对于使用持久对象缓存服务(比如 Memcached)的站点来说,在缓存失效之前,相同的数据库查询就不会再次运行,从而显著降低站点的数据库查询次数。

对于未使用内存缓存的站点来说,同个页面中相同的 WP_Query 也也不会重复执行,所以站点性能也会得到一定提升,但是不会那么显著。

WP_Query 实现数据库查询缓存是有一套自己的实现机制,所以对于插件和主题开发者来说,如果要对文章进行操作的话,最好使用 WordPress 提供的文章操作函数。

比如使用 wp_insert_post 函数将文章添加到数据库,这些函数会自动去清理缓存,这样下次使用 WP_Query 获取文章时候,就能获取是包含了新增的或者更新之后的数据。

如果直接使用 SQL 语句更新到数据库的话,则强烈建议执行之后,使用 clean_post_cache 函数对缓存进行清理。

如何禁用缓存

值得注意的是,在默认情况下,WP_Query 的所有数据库查询都将被缓存,如果想不缓存,只需将 cache_results 参数设置为 false 即可:

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

也可以通过 filter 全局禁用缓存:

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

禁用缓存只是在一些极端的情况下使用,为了获得最佳性能,强烈建议保持缓存开启,如果清理缓存使用 clean_post_cache 函数。

缓存 key 规则

缓存的键是使用 WP_Query 的查询参数生成的,但是会忽略以下参数:

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

因为这些忽略的参数不会影响生成的 SQL 语句,其中特别要注意下 fields 参数:

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

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

上面这两种情况,无论是否使用了 fields 参数或者什么参数,生成的 SQL语句都是一样的,然后查询结果都会被缓存起来,这样就能够更加高效的使用缓存,而无需存储多份缓存数据。

WP_Query 初始化所有作者数据

在 6.1 之前,主循环会同时加载作者信息,所以具有多个作者的站点需要执行多次数据库查询来分别获取作者的信息,现在 WordPress 6.1 引入了一个新函数 update_post_author_caches,通过在循环开始时调用该函数通过一次数据库查询就初始化所有用户(作者)缓存,而不是逐个加载每个用户,从而减少数据库查询。

此函数接受一个 post 对象数组参数,并将初始化用户缓存,WP_Query 会自定调用 update_post_author_caches 以提高站点性能。

初始化菜单中的所有链接对象

WordPress 6.1 还新增了函数 update_menu_item_cache,它支持一个 post 的对象数组参数,然后就会初始化菜单中引用到的文章或者分类的数据缓存,并且 WP_Query 也增加了一个新的参数 update_menu_item_cache,如果它被设置为 true,则会调用 update_menu_item_cache 函数仅仅通过两条 SQL 查询(一条是关于 posts,一条是关于 terms)就让 WP_Query 获取菜单信息。

get_page_by_title 直接使用 WP_Query

get_page_by_title 函数在 6.1 版本也改用 WP_Query 来获取数据,之前,该函数直接使用 SQL 查询来实现通过标题获取页面,从上可知,WP_Query 查询结果将会被缓存,这意味着现在调用 get_page_by_title 也将会被缓存,当然所有通过 WP_Query 获取数据的函数也将被缓存。