搜索全站
主题 11 分钟阅读

标签云小工具拖慢大站?wp_tag_cloud 参数与缓存方案

站点文章上万后,侧边栏标签云让每个页面多花几百毫秒。本文算清标签云的查询与渲染成本,给出参数收敛、片段缓存、对象缓存三条落地方案与缓存清理时机。

U
UFO_PF 2016-11-07 · 阅读 11 分钟
标签 主题
阅读提示
本文约 4,055 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:页脚那个「标签云」,正在悄悄吃你的首屏时间

做内容站的同学容易忽略一个不起眼的小东西:侧边栏或页脚挂着的「标签云」。站点只有几百篇文章时它跑得飞快;等到文章量爬到一两万、标签数涨到三五千,前台打开一篇普通文章都要等上三秒。装上 Query Monitor 一插,发现 wp_tag_cloud() 那一行占了七八百毫秒,页面总查询数从四十来条飙到一百多条。更麻烦的是它挂在公共区域,归档页、搜索页、单页全都一起慢。

很多人的第一反应是换「更轻量」的主题,但问题不在主题,而在标签云的默认行为:它默认拉取最多 45 个标签,为每个标签做一次字号映射计算,并且带 count 的取数会牵扯 wp_term_relationships 表。文章量一大,这个成本乘以页面数,数据库压力相当可观。本文给出三条处理路径:收敛参数、缓存输出片段、把标签归档页本身做成可分页可索引的列表。顺带说明一个常见误解——删除标签并不能提速,真正有效的是限制取数与缓存。

原理分析:成本到底花在哪几步

标签云的核心函数 wp_tag_cloud() 内部调用 get_terms(),默认 orderby 为 name、hide_empty 为 true,数量由小工具实例或参数决定,默认上限通常为 45。它的开销主要来自三处:

  1. 取数:wp_terms 与 wp_term_taxonomy 联查,带 count 时还要扫 wp_term_relationships,大表上是实打实的行数扫描;
  2. 排序与字号映射:拿到结果后做一次线性计算,把每个标签的文章数映射到 smallest~largest 的字号区间;
  3. 输出拼接:默认 format 为 flat,用 separator 拼字符串,本身不重,但前面两步已经花掉大头。

需要留意的是,标签云小工具是 WP_Widget_Tag_Cloud 的实例,参数在进入 wp_tag_cloud() 之前会经过 widget_tag_cloud_args 过滤器——这是我们最省事的介入点,完全不用改主题文件,子主题里加几行即可。

环节 默认行为 主要成本 优化动作
取数 取 45 个标签并统计文章数 三表联查 number 降到 20~30
排序 orderby=name 额外排序开销 改 orderby=count,取热门标签
渲染 每个标签算一次字号 PHP 线性计算 固定字号,smallest=largest
缓存 无,每次请求重算 全站公共区重复消耗 set_transient 缓存 6~12 小时
落地页 ?tag=xxx 无分页、权重分散 爬虫抓取浪费 自定义归档模板 + 分页

如果站点已经上了 Redis 之类的持久化对象缓存(可参见 WordPress 对象缓存配置),get_terms() 的结果会进入 wp_cache 组,第二轮请求基本不再打数据库;但字号计算和字符串拼接仍在每次请求里重跑,所以片段缓存依然值得做。

保姆级实操

  1. 先量化:装 Query Monitor,打开任意单页,切到「Queries by Component」,确认慢查询是否集中在 wp_tag_cloud,并记录「Time」列的实际耗时,别凭感觉动手。
  2. 收敛参数:用 widget_tag_cloud_args 把数量压到 25 左右,排序改成按文章数倒序,字号固定为单一值,直接省掉映射计算。
  3. 加片段缓存:把标签云输出整体塞进 transient,键名用参数哈希区分,缓存 12 小时。侧边栏内容本来就不是实时的,延迟半天完全可接受。
  4. 设置清理时机:在 set_object_terms、edited_term、deleted_term 等钩子上清缓存,否则新标签可能半天不上墙。
  5. 标签归档页分页:给标签归档套一个统一模板,限制每页条数并加 paginate_links(),避免单个大标签页一次吐出几百条。
  6. 瘦身标签本身:合并同义标签,删除只有一两篇文章的稀疏标签。这一步是给爬虫减负,和首屏速度关系不大,别把它当成加速手段。
<?php
// 子主题 functions.php:收敛标签云参数
add_filter( 'widget_tag_cloud_args', function( $args ) {
    $args['number']    = 25;
    $args['orderby']   = 'count';
    $args['order']     = 'DESC';
    $args['smallest']  = 14;
    $args['largest']   = 14;   // 字号固定,省掉映射计算
    $args['unit']      = 'px';
    $args['taxonomy']  = array( 'post_tag' );
    $args['hide_empty'] = true;
    return $args;
} );
<?php
// 片段缓存:整段标签云缓存 12 小时
function mimangfei_cached_tag_cloud( $args = array() ) {
    $args = wp_parse_args( $args, array(
        'taxonomy' => 'post_tag',
        'number'   => 25,
        'echo'     => false,
        'orderby'  => 'count',
        'order'    => 'DESC',
    ) );

    $key  = 'mftc_' . md5( wp_json_encode( $args ) );
    $html = get_transient( $key );

    if ( false === $html ) {
        $html = wp_tag_cloud( $args );
        set_transient( $key, $html, 12 * HOUR_IN_SECONDS );
    }

    echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
<?php
// 标签变动时清理缓存
add_action( 'set_object_terms', 'mimangfei_flush_tag_cloud' );
add_action( 'edited_term', 'mimangfei_flush_tag_cloud' );
add_action( 'deleted_term', 'mimangfei_flush_tag_cloud' );
function mimangfei_flush_tag_cloud() {
    global $wpdb;
    $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_mftc\_%'" );
    $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\_transient\_timeout\_mftc\_%'" );
    wp_cache_flush();
}

常见踩坑

  • 只改主题里的 sidebar.php:下次主题升级就被覆盖。过滤器写进子主题 functions.php,升级不受影响。
  • 缓存键不带参数:站点有多处标签云(侧边栏、页脚、移动端)时,不同参数会互相覆盖,出现页脚显示侧边栏内容的错乱。键名务必带参数哈希。
  • 缓存时间设成永久:set_transient 第三参数传 0 表示永不过期,新标签永远不出现。用 HOUR_IN_SECONDS 这类常量显式写出来。
  • 忘了清理钩子:加了缓存却没有清理动作,运营同学反馈「新标签半天不显示」,最后只能手动清库。
  • 把 hide_empty 设成 false:空标签也被拉进来,数量限制形同虚设,反而更容易撞上大表扫描。
  • 案例:一个两万篇文章的影视资料站,侧边栏标签云让单页 SQL 耗时稳定在 900ms 左右。按上面的参数收敛 + 12 小时片段缓存改造后,同一页面降到 60ms 以内。三天后运营发现新建的标签不显示,排查发现是清理钩子里 SQL 的 LIKE 把下划线当成了通配符,_transient_mftc_% 匹配到了别的键——修正转义后正常。这个坑说明缓存是双刃剑,清理逻辑必须和写入逻辑成对验证。
  • 用 number=0 想「显示全部」:0 在某些版本里会被当成不限制,几万标签一次性渲染,页面直接超时。

总结

判断标签云要不要优化,看两个数:Query Monitor 里 wp_tag_cloud 的耗时是否超过 100ms,以及标签总数是否超过 500。满足任一条就动手,顺序是「先收敛参数 → 再加片段缓存 → 最后才考虑换对象缓存」。清理钩子必须和缓存同时上线,否则就是给自己埋一个「内容不更新」的工单。标签归档页的分页与模板统一可以参见 自定义文章类型归档页样式统一,两者用的是同一套模板层级机制;如果站点已经启用页面缓存,记得在发布新标签后清一次页面缓存,相关做法见 WordPress 缓存插件配置。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。