阐述 `get_transient()` 和 `set_transient()` 函数的源码,它们如何利用 `wp_options` 表实现键值对缓存,并支持过期时间?

各位观众老爷,晚上好!我是今天的主讲人,咱们今天的主题是 WordPress 的 transient API,也就是 get_transient()set_transient() 这对好基友,以及它们背后默默付出的 wp_options 表。准备好,我们要开始扒它们的底裤了!

Transient API 是什么鬼?

想象一下,你有一个非常耗时的操作,比如从外部 API 获取数据,或者进行复杂的数据库查询。每次用户访问页面都要重新执行这些操作,那服务器岂不是要累死了?Transient API 就是来拯救世界的。它可以让你把这些耗时操作的结果缓存起来,下次用户访问的时候直接从缓存里拿,速度嗖嗖的!

Transient API 其实就是一个简单的键值对存储系统,它能让你设置缓存的过期时间,到期后缓存自动失效。

set_transient(): 种下缓存的种子

我们先来看看 set_transient() 函数的源码,它负责把数据存入缓存。

/**
 * Set the value of a transient.
 *
 * You can set the transient to expire in a number of seconds. If you set the
 * expiration to 0, the transient will not expire.
 *
 * @since 2.8.0
 *
 * @param string $transient  Transient name. Expected to not be SQL-escaped.
 *                           Must be 191 characters or less in length.
 * @param mixed  $value      Transient value. Must be serializable if non-scalar.
 *                           Expected to not be SQL-escaped.
 * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
 * @return bool False if value was not set and true if value was set.
 */
function set_transient( string $transient, mixed $value, int $expiration = 0 ): bool {
    /**
     * Fires before a transient is set.
     *
     * @since 3.0.0
     *
     * @param string $transient Transient name.
     * @param mixed  $value     Transient value.
     * @param int    $expiration Time until expiration in seconds.
     */
    do_action( 'set_transient', $transient, $value, $expiration );

    $transient_timeout = '_transient_timeout_' . $transient;
    $transient         = '_transient_' . $transient;

    if ( false === get_option( $transient_timeout ) ) {
        $autoload = 'yes';
        if ( $expiration ) {
            $autoload = 'no';
            add_option( $transient_timeout, time() + $expiration, '', $autoload );
        } else {
            add_option( $transient_timeout, 0, '', $autoload );
        }
    } else {
        if ( $expiration ) {
            update_option( $transient_timeout, time() + $expiration );
        } else {
            update_option( $transient_timeout, 0 );
        }
    }

    if ( false === get_option( $transient ) ) {
        $autoload = 'yes';
        add_option( $transient, $value, '', $autoload );
    } else {
        update_option( $transient, $value );
    }

    /**
     * Fires after a transient is set.
     *
     * @since 3.0.0
     *
     * @param string $transient Transient name.
     * @param mixed  $value     Transient value.
     * @param int    $expiration Time until expiration in seconds.
     */
    do_action( 'set_transient', $transient, $value, $expiration );

    return true;
}

看起来很长,但其实逻辑很简单:

  1. Action Hook: 首先,它会触发一个 set_transient 的 action hook,允许你在这个过程前后做一些事情。比如记录日志,或者修改缓存的值和过期时间。

  2. 命名空间: 它会给你的 transient name 加上前缀 _transient_timeout__transient_,这样可以避免和其他 option 冲突。比如,你想缓存名为 my_data 的数据,那么实际存储在 wp_options 表中的 option name 就是 _transient_my_data_transient_timeout_my_data

  3. 过期时间: 接下来,它会处理过期时间。如果设置了过期时间,它会将过期时间戳(当前时间 + 过期秒数)存储在 _transient_timeout_{transient_name} 这个 option 中。如果没有设置过期时间,就将 _transient_timeout_{transient_name} 设置为 0

  4. 存储数据: 最后,它会将你的数据存储在 _transient_{transient_name} 这个 option 中。

  5. add_option() vs update_option(): 函数会先尝试使用 get_option() 检查相应的 option 是否已经存在。如果不存在,就使用 add_option() 创建新的 option,否则使用 update_option() 更新已有的 option。

  6. Action Hook: 再次触发 set_transient action hook。

get_transient(): 挖出缓存的宝藏

现在,我们来看看 get_transient() 函数的源码,它负责从缓存中获取数据。

/**
 * Retrieve the value of a transient.
 *
 * If the transient does not exist, does not have a value, or has expired,
 * then the return value will be false.
 *
 * @since 2.8.0
 *
 * @param string $transient Transient name. Expected to not be SQL-escaped.
 * @return mixed Value of transient.
 */
function get_transient( string $transient ): mixed {
    /**
     * Fires before a transient is retrieved.
     *
     * @since 3.0.0
     *
     * @param string $transient Transient name.
     */
    do_action( 'get_transient', $transient );

    $transient_timeout = '_transient_timeout_' . $transient;
    $transient         = '_transient_' . $transient;

    $timeout = get_option( $transient_timeout );

    if ( false === $timeout ) {
        return false;
    }

    if ( (int) $timeout < time() && (int) $timeout > 0 ) {
        delete_transient( $transient );
        return false;
    }

    $value = get_option( $transient );

    /**
     * Filters an existing transient's value.
     *
     * The dynamic portion of the hook name, `$transient`, refers to the transient name.
     *
     * @since 2.8.0
     *
     * @param mixed $value Value of transient.
     */
    return apply_filters( "transient_{$transient}", $value );
}

同样,我们来分解一下:

  1. Action Hook: 触发 get_transient action hook。

  2. 命名空间:set_transient() 一样,它也会给 transient name 加上前缀 _transient_timeout__transient_

  3. 获取过期时间: 它会从 wp_options 表中获取 _transient_timeout_{transient_name} 的值,也就是过期时间戳。

  4. 检查是否过期: 如果过期时间戳小于当前时间,并且大于0(0表示永不过期),说明缓存已经过期。这时,它会调用 delete_transient() 函数删除缓存,并返回 false

  5. 获取数据: 如果缓存没有过期,它会从 wp_options 表中获取 _transient_{transient_name} 的值,也就是缓存的数据。

  6. Filter Hook: 最后,它会触发一个 transient_{$transient} 的 filter hook,允许你修改缓存的值。

delete_transient(): 清除缓存的痕迹

delete_transient() 函数的作用就是删除 transient,防止脏数据继续被使用。

/**
 * Delete a transient.
 *
 * @since 2.8.0
 *
 * @param string $transient Transient name. Expected to not be SQL-escaped.
 * @return bool True if the transient was successfully deleted, false otherwise.
 */
function delete_transient( string $transient ): bool {
    /**
     * Fires before a transient is deleted.
     *
     * @since 3.0.0
     *
     * @param string $transient Transient name.
     */
    do_action( 'delete_transient', $transient );

    $transient_timeout = '_transient_timeout_' . $transient;
    $transient         = '_transient_' . $transient;

    $deleted = delete_option( $transient );
    $deleted_timeout = delete_option( $transient_timeout );

    if ( $deleted || $deleted_timeout ) {

        /**
         * Fires after a transient is deleted.
         *
         * @since 3.0.0
         *
         * @param string $transient Transient name.
         */
        do_action( 'deleted_transient', $transient );

        return true;
    }

    return false;
}

这个函数更加简单粗暴:

  1. Action Hook: 触发 delete_transient action hook。
  2. 命名空间: 和之前一样。
  3. 删除 Option: 使用 delete_option() 函数分别删除 _transient_{transient_name}_transient_timeout_{transient_name} 这两个 option。
  4. Action Hook: 触发 deleted_transient action hook。

wp_options 表: 幕后英雄

Transient API 的所有数据都存储在 wp_options 表中。这个表是 WordPress 用来存储各种配置项的,比如站点标题、主题设置等等。

wp_options 表的结构通常包含以下几个关键字段:

字段名 数据类型 描述
option_id bigint(20) unsigned 主键,自增长。
option_name varchar(191) Option 的名称。Transient API 使用 _transient_{transient_name}_transient_timeout_{transient_name} 作为 option name。这个字段需要唯一索引。长度限制为 191 个字符,这是因为 WordPress 数据库的键长度限制。
option_value longtext Option 的值。Transient API 将缓存的数据存储在这个字段中。 longtext 可以存储大量数据,但是读取速度相对较慢。
autoload varchar(20) 指示这个 option 是否在 WordPress 初始化时自动加载。Transient API 默认将 _transient_timeout_{transient_name} 设置为 no (除非没有设置过期时间,设置为yes),将 _transient_{transient_name} 设置为 yes。 这意味着缓存的过期时间不会自动加载,而缓存的数据会。

Transient API 的优缺点

优点:

  • 简单易用: API 非常简单,只有三个函数,容易上手。
  • 自动过期: 可以设置过期时间,避免缓存脏数据。
  • 内置支持: 无需安装额外的插件,WordPress 自带。

缺点:

  • 性能瓶颈: 所有数据都存储在 wp_options 表中,如果缓存的数据量很大,或者频繁读写缓存,可能会导致性能问题。因为 wp_options 表通常存储在数据库中,读写速度相对较慢。
  • 数据类型限制: 存储在 wp_options 表中的数据需要能够被序列化。这意味着一些特殊的数据类型可能无法直接存储。
  • 全局共享: 所有插件和主题都可以访问和修改 transient 数据,容易产生冲突。

最佳实践

  • 谨慎使用: 只缓存真正需要缓存的数据,避免过度缓存。
  • 合理设置过期时间: 根据数据的更新频率,设置合适的过期时间。
  • 使用对象缓存: 如果缓存的数据量很大,可以考虑使用对象缓存,比如 Memcached 或 Redis。这些缓存系统可以将数据存储在内存中,读写速度更快。
  • 避免频繁读写: 尽量减少对 transient 的读写操作,可以使用批量操作或者其他优化手段。
  • 注意数据类型: 确保缓存的数据可以被序列化。

代码示例

<?php

// 设置一个 transient,过期时间为 30 分钟
$transient_name = 'my_expensive_data';
$data = get_transient( $transient_name );

if ( false === $data ) {
    // 如果缓存不存在,执行耗时操作
    $data = do_something_expensive();

    // 缓存数据,过期时间为 30 分钟 (1800 秒)
    set_transient( $transient_name, $data, 1800 );
}

// 使用缓存的数据
echo $data;

// 删除缓存
//delete_transient( $transient_name );

function do_something_expensive() {
    // 模拟一个耗时操作
    sleep(2);
    return 'Hello from the expensive function!';
}

总结

Transient API 是 WordPress 提供的一个简单易用的缓存机制。它通过 set_transient()get_transient()delete_transient() 这三个函数,以及 wp_options 表,实现了键值对缓存,并支持过期时间。 虽然 Transient API 有一些缺点,但在很多情况下,它仍然是一个不错的选择。只要合理使用,就可以有效地提高 WordPress 站点的性能。

好了,今天的讲座就到这里。希望大家对 Transient API 有了更深入的了解。 记住,缓存虽好,可不要贪杯哦! 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注