使用WP_Query构建高效的WordPress查询

使用WP_Query构建高效的WordPress查询

作为WordPress开发人员,我们经常需要从WordPress数据库中检索符合特定条件的文章、页面和其他内容。通常,我们不需要构建SQL查询(通常我们不应该),因为WP_Query类及其方法为我们提供了一种从数据库中检索数据的安全有效的方法。我们只需要声明一个参数数组,$query对象就会构建实际的SQL查询。

在这篇文章中,我将假设您已经了解WP_Query类的基础知识、它的方法和属性,以及在哪里可以找到可用变量的列表。

我们将重点介绍WP_Query类提供的参数,专门用于优化SQL查询,减少执行时间和资源消耗。

当流量和内容有限时,我们通常不会关心查询的效率。WordPress构建了优化良好的SQL查询,并提供了一个开箱即用的缓存系统。

当流量和网站内容显着增长时——多达数千个文章——那么我们必须考虑查询执行时间。

我们的工具箱

我将向您展示的代码已经通过Query Monitor测试,这是一个免费插件,提供有关查询性能、触发挂钩、HTTP请求、重写规则等的基本信息。

作为插件的替代方案,我们可以强制WordPress存储查询信息,在wp-config.php中声明以下常量:

define( 'SAVEQUERIES', true );

SAVEQUERIES设置为true时,WordPress会在$wpdb->queries数组中注册查询和一堆有用的信息。因此,调用者函数的名称和每个查询的执行间隔可以通过在像footer.php这样的模板文件中添加以下代码来打印:

if ( current_user_can( 'administrator' ) ) {
	global $wpdb;
	echo '<pre>';
	print_r( $wpdb->queries );
	echo '</pre>';
}

以下是回显内容的示例:

[4] => Array
(
	[0] => SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  WHERE 1=1  AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10
	[1] => 0.0163011550903
	[2] => require('wp-blog-header.php'), wp, WP->main, WP->query_posts, WP_Query->query, WP_Query->get_posts, QM_DB->query
	[trace] => QM_Backtrace Object
		( ... )
	[result] => 10
)

如果您想深入研究这个主题,请查看我们的教程: 编辑wp-config.php。最后,考虑到插件和内置SAVEQUERIES功能都是开发工具,我们应该在生产环境中关闭它们。

话虽如此,让我们来看看如何加速WordPress查询。

WP_Query – 为什么我们不计算行数

我们可以使用get_posts函数查询数据库,该函数返回一个文章数组或一个新的WP_Query对象实例。在这两种情况下,我们都可以通过为特定变量设置适当的值来确定查询的结果。

让我们从一个示例开始,该示例显示了通常出现在模板文件中的常见循环:

// The Query
$the_query = new WP_Query( $args );
// The Loop
if ( $the_query->have_posts() ) {
	while ( $the_query->have_posts() ) : $the_query->the_post(); 
		// Your code here
	endwhile;
} else {
		// no posts found
}
/* Restore original Post Data */
wp_reset_postdata();

$args是一个键/值对数组。这些对被命名为查询变量,并决定或影响实际的SQL查询。从插件查询数据库时,我们可能更喜欢使用pre_get_posts过滤器,如下例所示:

function myplugin_pre_get_posts( $query ) {
  if ( is_admin() || ! $query->is_main_query() ){
	return;
  }
  $query->set( 'category_name', 'webdev' );
}
add_action( 'pre_get_posts', 'myplugin_pre_get_posts', 1 );

这里要注意的重要一点是$query对象是通过引用传递的,而不是通过值传递,这意味着查询参数只是影响现有$query实例。

set方法将一个新的查询变量添加到查询规范中,并将强制WordPress从webdev类别中检索所有文章。这是结果查询:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts 
INNER JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1=1 
AND ( wp_term_relationships.term_taxonomy_id IN (12) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

在此示例中,LIMIT值已由管理员用户在阅读选项中设置,如下图所示。

WordPress阅读设置

在自定义查询中,由于分页参数, 我们可以设置posts_per_page要从数据库中检索的行数。

SQL_CALC_FOUND_ROWS选项强制查询计算找到的行数。该数字将由SQL函数FOUND_ROWS()返回,如以下示例所示:

SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
WHERE id > 100 LIMIT 10;

SELECT FOUND_ROWS();

不幸的是,SQL_CALC_FOUND_ROWS会显着减慢查询执行时间。好消息是我们可以强制WordPress删除提供未充分使用(且未记录)no_found_rows变量的选项。

如果省略了SQL_CALC_FOUND_ROWS,则FOUND_ROWS()将返回最大值为LIMIT的行数(MySQL文档中有关此主题的更多信息)。

在包含数百个文章的WordPress安装中,以下元查询耗时0.0107秒:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts 
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1 
AND ( ( wp_postmeta.meta_key = 'book_author'
AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%Isaac Asimov%' ) )
AND wp_posts.post_type = 'book'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

删除SQL_CALC_FOUND_ROWS,设置no_found_rows为false,相同的查询需要0.0006秒。比较有和没有SQL_CALC_FOUND_ROWS选项的两个查询

感谢Query Monitor插件,我们可以很容易地比较有和没有SQL_CALC_FOUND_ROWS选项的两个查询

wp_post表包含数千行时,查询执行可能需要几秒钟。当我们不需要分页时,我们应该设置no_found_rowstrue,使查询运行得更快。

缓存或不缓存

WordPress提供了一个开箱即用的内置缓存系统。虽然缓存通常会提高页面加载速度,但它可能会导致对数据库运行一些额外的查询。此外,无论何时执行查询,都可能会请求一堆不必要的数据。

幸运的是,WordPress允许我们提供三个特定参数来禁用缓存:

  • cache_results : 是否缓存文章信息。默认为true。
  • update_post_meta_cache:是否更新文章meta缓存。默认为true。
  • update_post_term_cache:是否更新文章term缓存。默认为true。

如果启用了持久缓存系统,例如Memcached,我们就不必关心缓存参数,因为WordPress默认会将这些参数设置为false。

在任何其他情况下,我们可以使用以下代码构建更快的查询:

function myplugin_pre_get_posts( $query ) {
  if ( is_admin() || ! $query->is_main_query() ){
	return;
  }
  $query->set( 'category_name', 'webdev' );

  $query->set( 'no_found_rows', true );
  $query->set( 'update_post_meta_cache', false );
  $query->set( 'update_post_term_cache', false );
}
add_action( 'pre_get_posts', 'myplugin_pre_get_posts', 1 );

当永久缓存系统不可用时,不应缓存返回少量数据的查询。

返回的字段

作为一般规则,我们永远不应该在数据库中查询不必要的字段。WP_Query类提供的字段参数,这允许限制返回字段的ID或 'id=>parent' 字段。源文件文档定义fields参数如下:

要返回的字段。单个字段或所有字段(字符串),或字段数组。’id=>parent’ 使用 ‘id’ 和 ‘post_parent’。默认所有字段。接受“ids”、“id=>parent”。

fields变量允许'ids''id=>parent',并且默认为 *(任何其他值),尽管您会注意到默认情况下WordPress会在多个查询中将该值设置为ids。最后,我们可以优化我们的第一个查询:

<?php
$args = array( 
	'no_found_rows' => true, 
	'update_post_meta_cache' => false, 
	'update_post_term_cache' => false, 
	'category_name' => 'cms', 
	'fields' => 'ids'
);
// The Query
$the_query = new WP_Query( $args );
$my_posts = $the_query->get_posts();

if( ! empty( $my_posts ) ){
    foreach ( $my_posts as $p ){
        // Your code
    }
}
/* Restore original Post Data */
wp_reset_postdata();
?>

当不需要特定字段时,将返回的字段限制为 ID。

小结

考虑到查询速度对于几百个帖子的小网站来说可能不会带来巨大的优势。如果您想为增长做好准备,或者您正在运行一个包含昂贵查询的大型网站,您应该优化您的 WordPress 查询。低效查询会显着减慢页面加载速度,但通过一些简单的调整,您可以大大加快您的网站速度。

评论留言