Transients API 简介
每个人都希望网站速度快。网站越快,用户停留的时间就越长,点击页面或购买产品的可能性就越大。加速网站的方法之一是实施缓存。缓存背后的理念是存储信息,以便访问速度比连接数据库更快。打个比方,去冰箱拿零食比开车去商店、在货架上找到它、付款然后开车回家吃要快得多。
缓存有多种类型:对象缓存、浏览器缓存、页面缓存甚至数据库缓存。每种类型都有其目的和设置步骤,范围从安装插件到更改服务器配置以启用对象缓存。在本文中,您将了解 Transients API,它可以是对象缓存或数据库缓存——具体取决于其使用位置。
Transients API 用于在 WordPress 中跨页面加载临时存储信息。在默认安装中,此信息存储在数据库的 options 表中。这与 Options API 非常相似,但增加了设置过期时间的能力,过期后数据将被删除。
Transients 是一个非常强大的工具,不仅可以避免对页面内容进行不必要的数据库请求,还可以加速生成复杂的标记并缓解缓慢的第三方 API 请求。
保存 Transient
要保存一个 transient,可以使用 <a href="https://developer.wordpress.org/reference/functions/set_transient/">set_transient</a> 函数。它接受三个参数:transient 的名称、要存储的数据以及一个可选的过期时间(以秒为单位)。如果 transient 设置成功,它将返回 true。
下面的代码片段设置了一个过期时间为一周的 transient。
set_transient( 'dev-blog-transient', 'Some data I want to persist', WEEK_IN_SECONDS );
WordPress 中有许多用于计算时间的常量,在处理 Transients API 时非常有用,并且使您的代码更具可读性。WEEK_IN_SECONDS 比 604800 更容易理解。
检索 Transient
要检索存储在 transient 中的数据,可以使用 get_transient。此函数接受一个参数——要检索的 transient 的名称——并返回数据,如果 transient 不存在或已过期,则返回 false。
$persisted_data = get_transient( 'dev-blog-transient' );
最好使用 === 运算符检查数据,以确认数据存在且类型相同。在某些情况下,transient 数据可能是“假值”,例如数字零或字符串“false”,但这对于数据的用途来说是有意义的。使用严格比较可以防止在这种情况下出现假阴性。
下面的代码片段演示了检查 transient 数据并设置重新生成 transient 能力的常见方法。
if ( false === ( $persisted_data = get_transient( 'dev-blog-transient' ) ) ) {
// 如果没有 transient 或它已过期,将运行此代码。
}
删除 Transient
任何 transient 都可以随时使用 delete_transient 函数删除。此函数也接受一个 transient 名称参数,如果 transient 被删除,将返回 true。
delete_transient( 'dev-blog-transient' );
对象缓存
在完全理解 Transients API 之前,您需要了解 WordPress 的内存或对象缓存系统是如何工作的。对象缓存允许您将计算成本高的项目存储在内存中供以后使用。
一个很好的用例是,当您有一个昂贵的 WP_Query,其结果需要多次使用时。缓存结果可以避免多次访问数据库检索数据,并加快处理速度。
WordPress 在幕后使用 WP_Object_Class 来驱动此缓存,但您会使用 wp_cache_* 系列的函数与之交互。
默认的对象缓存不是持久性的,这意味着任何已缓存的内容在页面加载完成后都会消失。可以使用许多缓存插件将对象缓存更改为持久性。设置此功能超出了本文的范围,但它通常需要修改服务器并添加 object-cache.php 插件来覆盖默认的对象缓存。
此时,您可能想知道这与 Transients API 有什么关系。嗯,一旦有了持久性缓存,Transients API 将不再通过 Options API 将数据存储在数据库中,而是使用 WP_Object_Class 来持久化其数据。最直接的好处是速度。访问存储在内存中的数据可能比查询数据库获取相同数据快得多。
最棒的是,您无需对代码进行任何更改即可获得性能优势。所有更改都在幕后处理。
Transient 过期
Transient 过期时间的概念常常被误解。Ryan McCue 在官方文档中有一个很好的解释:
似乎每个人都误解了 transient 过期的工作原理,简而言之就是:transient 过期时间是一个最大时间。没有最小年龄。Transient 可能在您设置后一秒消失,也可能在 24 小时后消失,但它们绝不会在过期时间之后还存在。
Ryan McCue
这是由于许多外部因素造成的,例如数据库更新或对象缓存被清除。因此,您应该始终假设 transient 不存在,并相应地编写代码。
这也意味着,即使您将 transient 的过期时间设置为从现在起 100 年,也不能保证数据会一直保留到那时。
始终设置过期时间
设置 transient 时,可以省略过期时间参数,使其默认为 0 秒。
set_transient( 'dev-blog-transient', 'Some data I want to persist' );
这实际上告诉 API,此数据在 0 秒后不应再可用。虽然这对于不需要很长时间的数据来说似乎是一个好方法,但它有一个潜在的副作用,仅当 transient 被保存到数据库时才会出现。
正如您现在所知,在幕后,transient 是通过 Options API 保存到数据库的。但是,如果您仔细查看源代码,您会发现任何没有过期时间的 transient 都会将相应的选项设置为自动加载。
$autoload = 'yes';
if ( $expiration ) {
$autoload = 'no';
add_option( $transient_timeout, time() + $expiration, '', 'no' );
}
$result = add_option( $transient_option, $value, '', $autoload );
这意味着这些数据将在每个页面请求时自动加载,即使从未使用过。想象一下,1000 个包含大型数据集的 transient 在每次页面加载时都被检索——这对您的网站性能有潜在的巨大影响。
避免此问题的最简单方法(除了使用对象缓存)是始终设置过期时间,即使只有一秒钟。
set_transient( 'dev-blog-transient', 'Persisted but not autoloaded data', 1 );
如果您没有使用对象缓存,并且需要在页面加载期间持久化数据但不需要 transient,请使用 wp_cache_set() 和 wp_cache_get() 来设置和检索在页面加载完成后会消失的数据。
何时应该使用 Transients?
这将取决于您的代码在哪里运行以及您是否了解环境。
在某些情况下,您会知道您的代码将在哪里运行。无论是客户网站还是您自己的网站。服务器配置对您是已知的,并且可以确定是否存在像 Redis 或 Memcached 这样的持久性对象缓存。在这种情况下,您可以使用 Transients API 或 wp_cache_* 函数,因为如上所述,transient 存储在对象缓存中。
如果您公开发布该插件或主题,则无法知道您的代码将在什么样的服务器环境中运行。在这种情况下,最好使用 Transients API,因为它保证会保存到数据库。
TL;DR: 如果您需要确保数据在任何服务器环境中都能持久化,请使用 Transient API。
代码示例
检索或重新生成 Transient 数据
最常见的例子可能是当您想检查一个 transient 并在它不存在时生成它。
在此示例中,您正在检索一个文章 ID 列表。如果 transient 返回 false,您将生成查询,然后使用 set_transient 仅存储查询返回的 ID 数组。这里重要的部分是,相同的变量名 $persisted_data 用于检索 transient 和最终生成的 ID 列表。这确保了无论 transient 是否存在,您的其余代码都能正常工作。
if ( false === ( $persisted_data = get_transient( 'dev-blog-post-id-list' ) ) ) {
// Transient 不存在或已过期,因此需要重新生成
$query = new WP_Query( array( 'posts_per_page' => 25, 'fields' => 'ids' ) );
if ( $query->have_posts() ) {
$persisted_data = $query->posts;
} else {
$persisted_data = array();
}
set_transient( 'dev-blog-post-id-list', $persisted_data, WEEK_IN_SECONDS );
}
预热缓存
一个真正出色的技术可以帮助确保您的网站访问者获得高性能体验,那就是“预热”您的缓存。这本质上意味着,当您的网站发生更改时,您可以重新生成任何相关的 transient,以便当用户访问您的网站时,它们已准备好被检索。
在此代码片段中,您将在保存文章时预热一个 transient。
add_action(
'save_post',
function () {
// 文章已保存。
$query = new WP_Query( array( 'posts_per_page' => 25, 'fields' => 'ids' ) );
if ( $query->have_posts() ) {
$persisted_data = $query->posts;
} else {
$persisted_data = array();
}
set_transient( 'dev-blog-post-id-list', $persisted_data, WEEK_IN_SECONDS );
},
);
存储标记
Transient 并不总是必须存储数据。在某些情况下,生成并存储一些标记可能性能更高。概念是相同的:检查 transient,如果不存在则重新生成标记并保存。
在这里,您将检索最近五篇文章并生成一个显示文章标题和作者姓名的列表。
if ( false === ( $markup = get_transient( 'stored_markup' ) ) ) {
$query = new WP_Query( array( 'posts_per_page' => 5 ) );
$markup = '';
if ( $query->have_posts() ) {
$markup .= '<ul>';
foreach ( $query->posts as $post ) {
$markup .= '<li>';
$markup .= get_the_title( $post->ID ) . ' | ';
$markup .= get_the_author_meta( 'nicename', $post->post_author );
$markup .= '</li>';
}
$markup .= '</ul>';
set_transient( 'stored_markup', $markup, WEEK_IN_SECONDS );
}
}
存储 API 响应
第三方 API 请求的响应是 transient 的绝佳候选者。您很少会希望每次页面加载都发出新的请求,事实上,将结果存储在 transient 中可以缓解 API 的速率限制或响应缓慢等问题。
在此代码片段中,您将向第三方 meme 生成器服务发出请求,并将 meme 数据存储在 transient 中。
if ( false === ( $api_data = get_transient( 'stored_memes' ) ) ) {
// 向 API 发出请求。
$response = wp_remote_get( 'https://api.imgflip.com/get_memes', array( 'headers' => array( 'Content-Type' => 'application/json') ) );
// 确保有响应。
if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) {
// 检索请求的主体。
$body = wp_remote_retrieve_body( $response );
// 处理 API 数据。
$api_data_json = json_decode( $body, true );
$api_data = $api_data_json['data']['memes'];
// 存储结果,最多 1 小时。
set_transient( 'stored_memes', $api_data, MINUTE_IN_SECONDS * 60 );
}
}