Web笔记 ·

优化WordPress性能的高级指南

前言

虽然玩wordpress,但对wordpress和php内部了解不多,这篇文章算是自己的视野扩展吧,不足之处,欢迎指出,老规矩,能力强的可以直接读原文。

本文原文:The Advanced Guide to Optimizing WordPress Performance

 

文章正文

今天,WordPress占据了互联网25%的位置,它很容易使用,令人难以置信的流行,而且不论何时何地。

但WordPress可能很慢,那么你如何优化呢?

有关如何调整和优化WordPress的文章有很多。事实上,WordPress本身提供了一个强大的WordPress优化指南

在大多数情况下,这些文章和教程涵盖了相当基本但有用的概念,例如使用缓存插件,使用CDNs(integrating with content delivery networks),并最大限度地减少请求。虽然这些提示是非常有效的,甚至是必要的,但是最终他们并没有解决潜在的问题:大部分缓慢的WordPress网站都是由于代码不好或效率低下而导致的。

优化WordPress性能的高级指南

WordPress可能很慢,但不是一定。(WordPress can be slow, but it doesn't have to be.)

 因此,本文主要针对开发人员提供一些指导方针,以帮助他们了解许多WordPress性能问题的根本原因并解决 它们。

WordPress提供了许多开发者们经常忽略的面向性能的的功能性代码( performance-oriented features)。不利用这些功能性的代码将会使一些简单的任务变得缓慢,例如获取帖子(posts)。本文详细介绍了四种合理的解决方案,它们解决了缓慢的WordPress性能背后的一些潜在问题。

获取帖子(Fetching Posts)

WordPress提供从数据库中获取任何类型的帖子(post)的方法。这有三种基本的方法:

使用query_posts() 方法

这是非常直接的方法,但问题是它覆盖了主查询,这可能会导致一些不便。例如,如果我们想要在获取帖子( 比如内部的 footer.php)之后某个的某个时间点确定我们正在处理什么样的页面,这时可能就是一个问题。事实上,官方文档中有一个建议不要使用此功能的注释,因为你需要调用附加方法来恢复原始查询。而且,替换主查询会对页面加载时间产生负面影响。

 使用 get_posts() 方法

这个和 query_posts()很像,但它不会修改主查询。另一方面,get_posts()默认情况下的查询,参数 suppress_filters设置为 true.。这可能会导致不一致,特别是如果我们在代码中使用查询相关的过滤器,因为你在页面中不期望的帖子可能会被该函数返回。

使用WP_Query

在我看来,这是从数据库中检索帖子的最佳方式。它不会更改主查询,它以其标准方式执行,就像任何其他WordPress查询一样。

但无论采用何种方式与数据库进行交互,都还有其他需要考虑的事项。

限制查询(Limiting the Query)

我们应该始终指定我们的查询必须获取多少帖子。

为了实现这一点,我们使用posts_per_page参数。

WordPress允许我们将-1表示为该参数的合理值,在这种情况下,系统将尝试获取满足定义条件的所有帖子。

这不是一个好的做法,即使我们确信我们只会得到一些结果作为回应。

一方面,我们很少能确定只能得到几个结果。即使我们可以,设置没有限制将需要数据库引擎扫描整个数据库寻找匹配。

相反,限制结果往往使得数据库引擎只能部分扫描数据,这将转化为较少的处理时间和更快的响应。

此外,默认情况下wordpress会尝试引入置顶帖子(sticky posts),并计算查询中找到的行数,这可能会对性能产生不利影响。

通常,我们并不真的需要这些信息。添加这两个参数将禁用这些功能并加快查询速度:

$query = new WP_Query( array(
	'ignore_sticky_posts'	=> true,
	'no_found_rows'		=> true
	)
);

从查询中排除帖子(Excluding Posts from the Query)

优化WordPress性能的高级指南

有时我们要从查询中排除某些帖子。WordPress提供了一个非常直接的方法:使用post__not_in参数。例如:

$posts_to_exclude 	= array( 1, 2, 3 );
$posts_per_page	= 10;


$query = new WP_Query( array(
	'posts_per_page'	=> $posts_per_page,
	'post__not_in'		=> $posts_to_exclude
	)
);


for ( $i = 0; $i < count( $query->posts ); $i++ ) {
	//do stuff with $query->posts[ $i ]
}

这很简单,但不是最优的。因为它在内部生成一个子查询。特别是在大型安装(large installations)中,这可能导致响应缓慢。通过一些简单的修改,让PHP解释器(interpreter )完成该处理更快:

$posts_to_exclude 	= array( 1, 2, 3 );
$posts_per_page	= 10;


$query = new WP_Query( array(
	'posts_per_page'	=> $posts_per_page + count( $posts_to_exclude )
	)
);


for ( $i = 0; $i < count( $query->posts ) && $i < $posts_per_page; $i++ ) {
	if ( ! in_array( $query->posts[ $i ]->ID, $posts_to_exclude ) ) {
		//do stuff with $query->posts[ $i ]
	}
}

我在那里做了什么?

基本上,我从数据库引擎中脱掉了一些工作,而是将其转移到PHP引擎,功能和在数据中处理的相同但在内存中,因此更快。

如何做的?

首选,我在查询中删除了post__not_in参数。

由于查询可能会给我们带来一些我们不希望的帖子,所以我增加了posts_per_page参数。这样,我确保,即使我在响应( response)中有一些不需要的帖子,我至少会有一些$posts_per_page期望的帖子。

然后,当我循环这些帖子时只处理那些不在$posts_to_exclude数组里的。

避免复杂的参数化(Avoiding Complex Parameterization)

所有这些查询方法提供了各种各样的提取帖子的方法:按类别(by categories),元键或值(by meta keys or values),按日期(by date),作者(by author)等。

虽然这种灵活性是一个强大的功能,但应谨慎使用,因为参数化可能会转化为复杂的表连接和昂贵的数据库操作。

在下一节中,我们将概述一种在不影响性能的情况下仍然实现类似功能的优雅方式。

挤压最多的WordPress选项(Squeezing the Most out of WordPress Options)

WordPress的选项API提供了一系列的工具,轻松加载或保存数据。处理小信息很有用,WordPress提供的其他机制(如帖子[posts]或分类[taxonomies])过于复杂。

优化WordPress性能的高级指南

例如,如果我们要存储验证密钥或我们网站头文件的背景颜色,那么选项(options )就是我们正在寻找的。

WordPress不仅给我们处理它们的方法,而且还使我们以最有效的方式这样做。

一些选项甚至在系统启动时直接加载,从而为我们提供更快的访问(创建新选项时,我们需要考虑是否要自动加载它)。

例如,考虑到我们有一个轮播显示在后端指定的突发新闻的站点。我们的第一本能就是使用一个元键(a meta key ),如下所示:

// functions.php
add_action( 'save_post', function ( $post_id ) {
	// For simplicity, we do not include all the required validation before saving
	// the meta key: checking nonces, checking post type and status, checking
	// it is not a revision or an autosaving, etc.
	update_post_meta( $post_id, 'is_breaking_news', ! empty ( $_POST['is_breaking_news'] ) );
} );


// front-page.php
$query = new WP_Query( array(
	'posts_per_page'	=> 1,
	'meta_key'		=> 'is_breaking_news'
	)
);
$breaking_news = $query->posts[0] ?: NULL;

正如你所看到的,这种方法很简单,但并不是最佳的。它将执行一个数据库查询,试图找到一个具有特定元键(meta key)的帖子。我们可以使用一个选项(option )来实现类似的结果:

// functions.php
add_action( 'save_post', function ( $post_id ) {
	// Same comment for post validation
	if ( ! empty ( $_POST['is_breaking_news'] ) )
		update_option( 'breaking_news_id', $post_id );
} );


// front-page.php
if ( $breaking_news_id = get_option( 'breaking_news_id' ) )
	$breaking_news = get_post( $breaking_news_id );
else
	$breaking_news = NULL;

从一个例子到另一个的功能稍有不同。

在第一段代码中,我们将始终按post的发布日期获得最新的突发新闻。

第二个,每当一个新的post被定为突发新闻,它将覆盖以前的突发新闻。

但是因为我们一次可能想要一个突发新闻,这不应该是一个问题。

最后,我们将一个重型数据库查询(使用WP_Query元键)更改为简单直接的查询(调用get_post()),这是一个更好和更有效的方法。

我们也可以做一个小的改变,并使用transients 而不是options。

Transients 工作类似,但允许我们指定一个到期时间。

例如,对于突发新闻,这就像戴手套一样合适,因为我们不想把一个旧帖子作为突发新闻,如果我们把改变或消除这个突发新闻的任务交给管理员,他可能会忘记做它。所以,通过两个简单的更改,我们添加一个到期日期:

// functions.php
add_action( 'save_post', function ( $post_id ) {
	// Same comment for post validation
	
	// Let's say we want that breaking news for one hour
	// (3600 =  # of seconds in an hour).
	if ( ! empty ( $_POST['is_breaking_news'] ) )
		set_transient( 'breaking_news_id', $post_id, 3600 ); 
} );


// front-page.php
if ( $breaking_news_id = get_transient( 'breaking_news_id' ) )
	$breaking_news = get_post( $breaking_news_id );
else
	$breaking_news = NULL;

启用持久缓存(Enable Persistent Caching)

WordPress本身有一个对象缓存机制

例如,使用该机制来缓存Options。

但是,默认情况下,缓存不是持久的,这意味着它仅在单个请求的持续时间内生效。所有数据都被缓存在内存中,以便更快的访问,但只有在该请求期间可用。

优化WordPress性能的高级指南

支持持久缓存需要安装一个持久缓存插件。

一些全页缓存插件包含一个持久的缓存插件(例如W3 Total Cache),但其他的没有,我们需要单独安装。

无论我们是使用文件,Memcached还是其他机制来存储缓存的数据,都将取决于我们平台的架构,但是我们应该利用这个惊人的功能。

有人会问:“如果这是一个很好的功能,为什么WordPress没有默认启用它”?

主要的原因是,根据我们平台的体系结构,一些缓存技术将会工作,而其他缓存技术将不会。

例如,如果我们在我们的分布式服务器中托管我们的站点,我们应该使用外部缓存系统(如Memcached服务器),但是如果我们的网站驻留在单个服务器上,那么我们可以通过简单的使用文件系统实现缓存从而来节省一些钱。

我们需要考虑的一件事是缓存过期。这是使用持久缓存最常见的陷阱。

如果我们不正确地解决这个问题,我们的用户会抱怨说他们看不到他们所做的更改,或者他们的更改需要太长时间才能应用。

有时我们会发现自己在性能和动态之间进行权衡,但即使有这些障碍,持续缓存也是几乎每一个WordPress安装(installation )都应该利用的。

AJAXing the Fastest Way

如果我们需要通过AJAX与我们的网站沟通,WordPress在处理服务器端的请求时提供一些abstraction( offers some abstraction)

即使这些技术可以在编写后端工具或从前端提交表单时使用,如果不是必要的话,应该避免这些技术。

原因是为了使用这些机制,我们有义务向位于wp-admin文件夹内的某个文件发出请求。大多数(如果不是全部)WordPress全页缓存插件既不缓存发布请求也不调用管理员文件(administrator files)。

例如,如果我们在用户滚动我们的主页时动态加载更多的帖子,那么最好直接调用其他前端页面,这将获得被缓存的好处。

然后,我们可以通过浏览器中的JavaScript来解析结果。

是的,我们发送的数据比我们需要的更多,但是我们在处理速度和响应时间方面都取得了胜利。

摧毁WordPress就是缓慢的概念(Destroy the Notion That WordPress is Just Slow)

这些只是开发人员在编写WordPress时应考虑的几点建议。

有时,我们忘记我们的插件或主题可能需要与其他插件一起使用,或者我们的网站可能由托管公司提供服务,该公司为数百个或数千个其他网站提供通用数据库。

我们只关注插件应该如何运行,而不是如何处理该功能,或者如何以有效的方式进行操作。

从上面可以看出,在WordPress中表现不佳的根本原因是不好的(bad)的和低效率的代码。然而,WordPress通过其各种API提供了所有必要的功能,可以帮助我们构建更多性能更好的插件和主题,而不会影响整体平台的速度。

 

参与评论