WordPress 片段缓存再探

Avatar of Ryan Burnette
Ryan Burnette 发布

DigitalOcean 提供适用于您旅程各个阶段的云产品。立即开始使用 $200 免费信用额度!

以下文章由 Ryan Burnette 客串撰写。正如您将在下面阅读到的那样,Ryan 正在开发一个 WordPress 网站,该网站使用一个插件通过 Instagram API 获取照片。他以一种不太标准的方式使用它,这导致了大量的请求和非常缓慢的网站。在探索不同的解决方案时,他发现了片段缓存。但不幸的是,他发现了一些过时的信息,因此,作为一名优秀的开发者,他对其进行了更新。以下是背景故事和历程。

我们都知道网站性能非常重要。然而,对于构建自定义 WordPress 主题的开发者来说,在实际编写代码时,这在优先级列表中排名很低。渲染页面上元素的代码通常使用可用的函数以最简单、最友好的方式编写。这会导致代码易于创建、阅读和维护。它还会导致元素具有非常低效的渲染过程,其中包含多余的循环和数据库查询。

多出几毫秒真的会累积起来。再加上网站流量的增加,可能会出现严重的性能问题。

许多非常聪明的人已经将他们的智慧应用于这个问题。WordPress 社区已经开发了一些很棒的缓存插件。 W3 Total Cache 就是其中之一。我非常喜欢它们并经常使用它们,但有时我并不需要那么强大的功能。我可能想要避免配置,或者拥有不适合缓存的元素。此外,为了避免将来维护的麻烦,最好将插件数量保持在最低限度。

这促使我采用不同的方法。我想要使用少量代码来缓存页面上一些太笨拙而无法在每次加载时都渲染的元素。

片段缓存

当 WordPress 页面加载时,PHP 会被处理,MySQL 数据库会被查询。有时,一段代码会执行许多查询并需要一段时间才能运行。片段缓存会获取代码块的输出并将其存储,以便在预定的时间内使用。当代码运行时,只要时间限制没有超过,该代码块就会被忽略,并且存储的输出会被返回并打印到页面上。

片段缓存并不是什么新鲜事物。WordPress 核心开发者 Marc Jaquith 曾经写过关于片段缓存的文章。后来,我发现了一个简化了 Jaquith 类为函数的 Gist。我对此进行了分叉并进行了修改。

在 2.5 之前的 WordPress 版本中,WP_Cache 对象可以像 Jaquith 的示例中演示的那样用于持久缓存,或者比一次页面加载时间更长的缓存。 Transients API 可以创建具有方便的过期功能的持久数据库对象。我的片段缓存代码片段使用这种方法来存储片段。

以下几行代码可以包含在 functions.php 文件中,允许将任何输出作为片段缓存。以下是代码。

function fragment_cache($key, $ttl, $function) {
  if ( is_user_logged_in() ) {
    call_user_func($function);
    return;
  }
  $key = apply_filters('fragment_cache_prefix','fragment_cache_').$key;
  $output = get_transient($key);
  if ( empty($output) ) {
    ob_start();
    call_user_func($function);
    $output = ob_get_clean();
    set_transient($key, $output, $ttl);
  }
  echo $output;
}

该函数接受三个参数

  • 键:一个简单的字符串,用于标识片段。请注意,该函数会添加一个前缀以避免与其他瞬态冲突。您可以通过编辑函数或添加与 'fragmentcacheprefix' 标签匹配的过滤器来更改前缀。

  • 生存时间:缓存生存的时间(以秒为单位)。我通常使用 时间常量。例如,DAYINSECONDS 是 86400,表示一天中的秒数。这对于那些懒得进行简单数学运算的人来说非常有用。

  • 函数:创建输出的函数。这可以是任何东西,如本帖中的示例所示。

使用示例

使用片段缓存就像将一些 HTML 和 PHP 包含在函数中一样简单。

以下是一些开发者可能在 WordPress 网站或应用程序上编写的代码。

<p>Here's some HTML.</p>

<?php
// Here's some PHP
$args = array(
  'post_type' => 'my_data',
  'posts_per_page' => -1
);
$posts = get_posts($args);
foreach ( $posts as $p ) {
  echo '<pre>';
  echo get_post_meta($p,'some_meta',true);
  echo '</pre>';
}?>

<p>The PHP in this block runs and executes queries with every page load. :(</p>

以下是使用片段缓存代码片段实现的相同代码。请注意,我们正在使用 HTML 和 PHP,这些会被函数捕获并缓存。

让我们回顾一下该函数的三个参数

  • 表示缓存的标签。这里有一个提示。如果此代码因页面而异,请将帖子 ID 连接到标签中,为每个页面创建单独的缓存。如果主循环被片段缓存,这一点非常重要。
  • 超时时间。我通常使用 WordPress 时间常量,但可以使用任何以秒为单位的时间。
  • 输出代码本身。请注意,它被包含在一个函数中。该函数被传递到片段缓存函数中。没错,您可以在 PHP 中将函数作为参数传递。
<?php
// After
fragment_cache('my_footer', DAY_IN_SECONDS, function() { ?>

<p>Here's some HTML.</p>

<?php
// Here's some PHP
$args = array(
  'post_type' => 'my_data',
  'posts_per_page' => -1
);
$posts = get_posts($args);
foreach ( $posts as $p ) {
  echo '<pre>';
  echo get_post_meta($p,'some_meta',true);
  echo '</pre>';
}
?>

<p>And everything this block outputs will be fragment cached. :)</p>

<?php }); ?>

示例

以下是一些我使用此函数来避免数据库比实际需要更频繁地渲染元素的示例。

自定义页脚

我实现此函数的最常见位置是自定义页脚。我经常创建一个页脚,其中不仅包含 WordPress 菜单,还包含基于 get_posts() 函数生成的菜单,以及每个迭代帖子的附加 get_post_meta() 函数。我发现很多情况下,渲染一个大的页脚需要 100-200 毫秒。片段缓存使这些元素的加载时间变得无关紧要。

数据表

WordPress 越来越受欢迎,成为了一种应用程序开发平台。现在有很多关于它的热议。无论你是否喜欢,人们都将使用 WordPress 构建应用程序。这通常会导致原本应该是一组数据库对象的情况变成存储在自定义帖子类型的帖子中。每个属性都成为该帖子的元数据的一部分,而不是真实数据库对象的属性。查询和渲染以这种方式存储的数据表需要很长时间。片段缓存它可以解决这个问题。

令人尴尬的冗长循环

有数千个令人尴尬的冗长且复杂的循环。我也写过一些。无论你写了多么低效的代码片段,你都可以将其放入片段缓存中,它将快速加载。

一个测试用例

我是 STUDIOCRIME 的网站管理员,该网站聚合了街头艺术视频。 2019 年 12 月更新:链接已删除,因为该网站已不存在,现在是垃圾邮件网站。

WordPress 为我们的策展人提供了一个很棒的、简单的 CMS,供他们在发布和组织网站的视频内容时使用。视频集页面每次查看时都会加载超过 80 个帖子。每次迭代还会查询数据库以获取帖子元数据。

我们还在侧边栏中显示大量内容,使用一个插件进行身份验证并从 Instagram API 获取数据。该插件并非旨在以我们使用它的方式使用。每个小工具都会单独实例化该插件。这会导致非常长的加载时间。

它确实非常快速且易于构建,但这里和那里的几毫秒加起来,导致页面需要 1500 到 5000 毫秒才能渲染。五秒钟对于等待网页加载来说太久了。

我们选择不使用像 W3 Total Cache 这样的缓存插件,因为关于页面如何加载以及在 PHP 中跟踪用户数据的决定。页面缓存会阻止此 PHP 运行。

这提供了一个绝佳的机会,既可以使用片段缓存,又可以测试缓存加载速度慢的片段所带来的收益。

我使用 Apache Bench 运行了这些测试。Apache Bench 以并发或连续的方式发出一个或多个请求,并报告 Web 服务器提供页面所花费的时间。请注意,在没有缓存的情况下,单个请求的加载时间大约长三倍。如果有多个请求,响应时间会变得很高,达到 3 到 5 秒。对网站的慢速部分进行片段缓存使时间恢复正常,并在更重的负载下为我们提供了所需的性能。

这些测试展示了在 10、100 和 1000 个并发请求的情况下,单个页面的渲染时间。

Apache Bench 测试 无缓存 有缓存
10 个请求 1426 毫秒 518 毫秒
100 个请求 3498 毫秒 658 毫秒
1000 个请求 5116 毫秒 895 毫秒

祝您缓存愉快!