分析 WordPress `_prime_post_caches()` 函数的源码:解释它如何预加载文章元数据来减少数据库查询。

各位观众,大家好!我是你们今天的讲师,外号“代码诗人”。今天咱们不吟诗作对,来聊聊WordPress里一个幕后英雄——_prime_post_caches()函数。

它就像一个勤劳的管家,默默地为文章元数据预加载,让你的网站运行速度嗖嗖的。 今天咱们就深入剖析一下这个管家,看看它到底是怎么干活的,以及咱们怎么才能更好地利用它。

一、 什么是“预加载”?为什么要预加载?

想象一下,你开了一家图书馆,读者络绎不绝。 每次读者来借书,你都要从书架上找到书,然后登记借阅信息。 如果读者一次借很多书,你就要跑很多趟书架,登记很多次。 这样效率是不是很低?

预加载就像是提前把读者要借的书都准备好,放在前台的桌子上。 读者来了直接拿,不用你再跑腿。 这样效率就大大提高了。

在WordPress里,文章元数据(比如标题、内容、作者、分类等等)都存储在数据库里。 每次访问一个页面,WordPress都要从数据库里查询这些数据。 如果一个页面有很多文章,或者一个文章有很多元数据,就要查询很多次数据库。 这会严重影响网站的性能。

_prime_post_caches() 函数的作用就是预加载文章元数据,把它们提前放到缓存里。 这样,WordPress在访问页面的时候,就可以直接从缓存里读取数据,而不用每次都查询数据库。 这就像提前把书准备好一样,大大提高了效率。

二、 _prime_post_caches() 函数的源码分析

废话不多说,直接上代码! (以下代码是简化后的版本,方便理解)

/**
 * Pre-loads caches for posts.
 *
 * @since 2.1.0
 *
 * @param array|int|WP_Post $posts Array of post IDs or post objects
 *                                  or a single post ID or post object.
 * @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
 * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
 */
function _prime_post_caches( $posts, $update_term_cache = true, $update_meta_cache = true ) {
    if ( ! is_array( $posts ) ) {
        $posts = array( $posts );
    }

    $post_ids = array();
    foreach ( $posts as $post ) {
        if ( is_object( $post ) ) {
            $post_id = $post->ID;
        } else {
            $post_id = (int) $post;
        }

        if ( ! $post_id ) {
            continue;
        }

        $post_ids[] = $post_id;
    }

    $post_ids = array_unique( $post_ids ); // 去重
    $post_ids = array_filter( $post_ids ); // 过滤空值

    if ( empty( $post_ids ) ) {
        return;
    }

    // Step 1: 预加载文章对象本身
    _prime_posts( $post_ids );

    // Step 2: 预加载文章的术语(分类、标签等)
    if ( $update_term_cache ) {
        update_term_cache( $post_ids );
    }

    // Step 3: 预加载文章的元数据 (自定义字段)
    if ( $update_meta_cache ) {
        update_post_meta_cache( $post_ids );
    }
}

咱们来一行一行地解读一下:

  1. 参数处理:

    • $posts: 接受一个数组,里面可以是文章ID,也可以是文章对象(WP_Post)。 如果传入的是单个文章ID或对象,函数会把它转换成数组。
    • $update_term_cache: 是否更新术语缓存(分类、标签等)。 默认是true
    • $update_meta_cache: 是否更新元数据缓存(自定义字段)。 默认是true
  2. 提取文章ID:

    • 遍历$posts数组,提取出文章ID。 如果元素是文章对象,就从$post->ID获取ID。 如果元素是数字,就把它转换成整数。
    • 对文章ID进行去重和过滤空值,确保$post_ids数组里都是有效的文章ID。
  3. 预加载文章对象:

    • 调用_prime_posts( $post_ids )函数,预加载文章对象本身。 这个函数会从数据库里批量查询文章信息,并把它们存储到对象缓存里。
  4. 预加载术语:

    • 如果$update_term_cachetrue,就调用update_term_cache( $post_ids )函数,预加载文章的术语(分类、标签等)。 这个函数会查询文章所属的术语,并把它们存储到术语缓存里。
  5. 预加载元数据:

    • 如果$update_meta_cachetrue,就调用update_post_meta_cache( $post_ids )函数,预加载文章的元数据(自定义字段)。 这个函数会查询文章的自定义字段,并把它们存储到元数据缓存里。

三、 _prime_posts() 函数:文章对象的预加载

_prime_posts() 函数负责预加载文章对象本身。 咱们也来看一下它的源码:

/**
 * Fill the cache for multiple posts.
 *
 * @since 4.6.0
 *
 * @param array $post_ids Array of post IDs.
 */
function _prime_posts( $post_ids ) {
    global $wpdb;

    $non_cached_ids = _get_non_cached_ids( $post_ids, 'posts', 'WP_Post' );

    if ( ! empty( $non_cached_ids ) ) {
        $fresh_posts = array();
        $query = "SELECT * FROM {$wpdb->posts} WHERE ID IN (" . implode( ',', array_map( 'intval', $non_cached_ids ) ) . ")";
        $results = $wpdb->get_results( $query );

        foreach ( $results as $row ) {
            $post = sanitize_post( $row, 'db' );
            wp_cache_add( $post->ID, $post, 'posts' );
            $fresh_posts[ $post->ID ] = $post;
        }

        // Prime term caches for any new posts.
        update_term_cache( array_keys( $fresh_posts ) );
        update_post_meta_cache( array_keys( $fresh_posts ) );
    }
}
  1. _get_non_cached_ids() 这个函数负责找出哪些文章ID还没有被缓存。 它会查询对象缓存,看看哪些ID对应的文章对象不存在。
  2. 数据库查询: 对于没有被缓存的文章ID,函数会构建一个SQL查询,从数据库里批量查询这些文章的信息。
  3. 创建文章对象: 遍历查询结果,把每一行数据转换成一个WP_Post对象。
  4. 添加到缓存: 使用wp_cache_add()函数,把文章对象添加到对象缓存里。 缓存的key是文章ID,group是'posts'
  5. 更新术语和元数据缓存: 对于新获取的文章,会调用update_term_cache()update_post_meta_cache()来预加载它们的术语和元数据。

四、 update_term_cache() 函数:术语的预加载

update_term_cache() 函数负责预加载文章的术语(分类、标签等)。

/**
 * Updates the term cache for multiple posts.
 *
 * @since 2.3.0
 *
 * @param array $post_ids Array of post IDs.
 * @return bool|array Returns false if caching is disabled, otherwise returns an array of taxonomy names.
 */
function update_term_cache( $post_ids = array() ) {
    global $wpdb;

    $term_ids = array();
    $taxonomies = get_taxonomies();
    $terms = array();

    foreach ( $taxonomies as $taxonomy ) {
        $terms[ $taxonomy ] = array();
    }

    $object_ids = array_map( 'intval', $post_ids );
    $object_ids = implode( ',', $object_ids );

    // Query to find all term relationships.
    $query = "SELECT tr.object_id, tt.term_id, tt.taxonomy
        FROM {$wpdb->term_relationships} AS tr
        INNER JOIN {$wpdb->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
        WHERE tr.object_id IN ($object_ids)";

    $term_relationships = $wpdb->get_results( $query );

    if ( $term_relationships ) {
        foreach ( $term_relationships as $term_relationship ) {
            $object_id = intval( $term_relationship->object_id );
            $term_id = intval( $term_relationship->term_id );
            $taxonomy = $term_relationship->taxonomy;

            if ( ! isset( $terms[ $taxonomy ] ) ) {
                $terms[ $taxonomy ] = array();
            }

            if ( ! isset( $terms[ $taxonomy ][ $object_id ] ) ) {
                $terms[ $taxonomy ][ $object_id ] = array();
            }

            $terms[ $taxonomy ][ $object_id ][] = $term_id;
            $term_ids[] = $term_id;
        }

        $term_ids = array_unique( $term_ids );
        _prime_terms( $term_ids );

        foreach ( $taxonomies as $taxonomy ) {
            wp_cache_set( 'get_object_terms', $terms[ $taxonomy ], 'terms' );
        }
    }

    return $taxonomies;
}
  1. 查询术语关系: 函数构建一个SQL查询,从wp_term_relationships表和wp_term_taxonomy表里查询文章和术语之间的关系。
  2. 整理数据: 遍历查询结果,把文章ID、术语ID和分类法(taxonomy)信息整理到一个数组里。
  3. 预加载术语对象: 调用_prime_terms()函数,预加载术语对象。 这个函数会从数据库里批量查询术语信息,并把它们存储到术语缓存里。
  4. 设置对象术语缓存: 将每个分类法的对象术语关系存入 terms group下的 get_object_terms 缓存中。

五、 update_post_meta_cache() 函数:元数据的预加载

update_post_meta_cache() 函数负责预加载文章的元数据(自定义字段)。

/**
 * Updates the post meta cache for multiple posts.
 *
 * @since 2.9.0
 *
 * @param array $post_ids Array of post IDs.
 * @return bool True on success, false on failure.
 */
function update_post_meta_cache( $post_ids ) {
    global $wpdb;

    $post_ids = array_map( 'intval', $post_ids );
    $post_ids = implode( ',', $post_ids );

    $cache = array();
    $meta_list = $wpdb->get_results( "SELECT post_id, meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id IN ($post_ids) ORDER BY meta_key", ARRAY_A );

    if ( ! empty( $meta_list ) ) {
        foreach ( $meta_list as $metarow ) {
            $mpid = intval( $metarow['post_id'] );
            $mkey = $metarow['meta_key'];
            $mval = $metarow['meta_value'];

            // Force subkeys to be array type:
            if ( ! isset( $cache[ $mpid ] ) )
                $cache[ $mpid ] = array();
            if ( ! isset( $cache[ $mpid ][ $mkey ] ) )
                $cache[ $mpid ][ $mkey ] = array();

            // Backwards compatibility:
            $cache[ $mpid ][ $mkey ][] = maybe_unserialize( $mval );
        }

        foreach ( $cache as $post_id => $value ) {
            wp_cache_set( $post_id, wp_slash_deep( $value ), 'post_meta' );
        }
    }

    return true;
}
  1. 查询元数据: 函数构建一个SQL查询,从wp_postmeta表里查询文章的元数据。
  2. 整理数据: 遍历查询结果,把文章ID、元数据键(meta_key)和元数据值(meta_value)整理到一个数组里。
  3. 添加到缓存: 使用wp_cache_set()函数,把元数据添加到元数据缓存里。 缓存的key是文章ID,group是'post_meta'

六、 总结:_prime_post_caches() 的工作流程

用一张表格来总结一下 _prime_post_caches() 的工作流程:

步骤 函数调用 功能描述 缓存 Group
1 参数处理 & ID 提取 提取文章ID,去重,过滤空值。 N/A
2 _prime_posts() 预加载文章对象本身。 posts
3 update_term_cache() 预加载文章的术语(分类、标签等)。 terms (get_object_terms)
4 update_post_meta_cache() 预加载文章的元数据(自定义字段)。 post_meta

七、 如何使用 _prime_post_caches()

WordPress会在很多地方自动调用 _prime_post_caches() 函数,比如:

  • 在主循环里,当循环输出文章的时候。
  • get_posts() 函数里,当获取文章列表的时候。
  • get_permalink() 函数里,当获取文章链接的时候。

但是,在某些情况下,你可能需要手动调用 _prime_post_caches() 函数,比如:

  • 当你使用自定义的查询方式获取文章列表的时候。
  • 当你需要预加载特定文章的元数据的时候。

使用方法很简单:

$post_ids = array( 1, 2, 3 ); // 要预加载的文章ID
_prime_post_caches( $post_ids );

或者

$posts = get_posts( array( 'numberposts' => 10 ) ); // 获取10篇文章
_prime_post_caches( $posts ); // 预加载这些文章的元数据

八、 性能优化建议

  1. 避免过度预加载: 不要预加载所有文章的元数据,只预加载当前页面需要的文章的元数据。 否则会浪费资源,甚至适得其反。
  2. 利用 Transient API: 对于一些不经常变化的元数据,可以考虑使用 Transient API 来缓存。 Transient API 比对象缓存更持久,可以减少数据库查询的次数。
  3. 使用 Redis 或 Memcached: 如果你的网站访问量很大,可以考虑使用 Redis 或 Memcached 作为对象缓存。 这些缓存系统比 WordPress 自带的对象缓存更高效,可以显著提高网站的性能。
  4. 审查你的自定义代码: 确保你的自定义代码没有重复查询数据库。 使用 WP_Queryget_posts() 函数时,要仔细检查参数,避免不必要的查询。

九、 总结

_prime_post_caches() 函数是 WordPress 里一个非常重要的性能优化工具。 它可以预加载文章元数据,减少数据库查询的次数,从而提高网站的性能。 理解 _prime_post_caches() 函数的工作原理,可以帮助你更好地优化 WordPress 网站。

希望今天的讲座能帮助大家更深入地理解 WordPress 的缓存机制,让你的网站跑得更快! 谢谢大家!

发表回复

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