跳到主内容

WordPress 对象缓存与 Redis 持久化:从 wp_cache_* 到 object-cache.php drop-in 的完整落地

100%
WordPress 对象缓存与 Redis 持久化:从 wp_cache_* 到 object-cache.php drop-in 的完整落地

WordPress 默认的对象缓存是「非持久化」的——它只在单次请求的内存里生效,请求结束即清空;要让缓存跨请求复用,必须在 wp-content/ 放一个 object-cache.php drop-in,把后端换成 Redis 或 Memcached 。没做这一步之前,Transients API 实际是把数据写进 wp_options 表;装上持久化后端后,同样的代码不改一行,数据就自动进内存。

很多站点做完页面缓存就不再管对象缓存,结果后台、 REST API 、未命中页面缓存的登录用户请求依然每次打满数据库。本文讲清对象缓存的定位、wp_cache_* 函数族、 drop-in 接管机制,以及 Transients 的三个真实陷阱。

先分清三层缓存

层级作用范围典型实现主要收益
对象缓存 Object Cache数据库查询结果、复杂计算值Redis / Memcached drop-in减少 SQL 查询次数
瞬时数据 Transients带过期时间的命名数据Transients API远程接口结果、定期刷新数据
页面缓存 Page Cache整页 HTML 输出Nginx fastcgi_cache 、插件跳过 PHP 与数据库
页面缓存解决”访客看到整页”的问题,对象缓存解决”绕过页面缓存的请求(登录用户、后台、 API 、动态片段)还在反复查库”的问题。两者互补,不能互相替代。

wp_cache_* 函数族怎么选

官方文档明确建议:不要直接实例化 WP_Object_Cache 类,一律用 wp_cache_* 函数,这样后端可替换。

  • wp_cache_get( $key, $group ) / wp_cache_set( $key, $data, $group, $expire ):最常用的一组。
  • wp_cache_add() / wp_cache_replace():存在性敏感场景,避免覆盖。
  • wp_cache_delete() / wp_cache_flush():删除单键、清空全部(flush 要慎用)。
  • wp_cache_get_multiple() / wp_cache_set_multiple():批量读写,循环里逐个 get 是典型性能反模式。
function qc_hot_posts() {
    $cache_key = 'hot_posts_' . date_i18n( 'Ymd' );
    $found     = false;
    $posts     = wp_cache_get( $cache_key, 'qicaiyun', false, $found );

    if ( false === $posts || ! $found ) {
        $posts = get_posts( array(
            'posts_per_page' => 10,
            'meta_key'       => 'views',
            'orderby'        => 'meta_value_num',
        ) );
        wp_cache_set( $cache_key, $posts, 'qicaiyun', HOUR_IN_SECONDS );
    }
    return $posts;
}

用自定义 group(如 qicaiyun)而不是默认 group,好处是后续可以按组做隔离清理。注意新版批量/分组函数要用能力探测,不能靠 function_exists 判断——核心会做 polyfill:

if ( wp_cache_supports( 'flush_group' ) ) {
    wp_cache_flush_group( 'qicaiyun' );
} else {
    wp_cache_flush();
}

不看 wp_cache_supports() 就直接调 wp_cache_flush_group(),后端不支持时会退化成全站清空——那是缓存雪崩。

drop-in:为什么多放一个文件就持久化了

WordPress 启动时会检查 wp-content/object-cache.php;如果存在,就不加载核心自带的 wp-includes/cache.php 实现,而是用你的 drop-in 接管后端。所有 wp_cache_* 调用因此自动走 Redis 。

常见持久化后端:

后端依赖适合场景
Redis Object CacheRedis 服务 + PHP 扩展单机 / 主从,最主流
Memcachedmemcached 服务 + 扩展纯内存场景,多机水平扩展
SQLite Object CacheSQLite3 扩展共享主机、无 Redis 环境
Docket CachePHP OPcache轻量方案,省一个外部服务
一个历史坑:WP_CACHE 常量(展开)
WordPress 2.5 之前,在 wp-config.php 里写 define( 'WP_CACHE', true ); 就能持久化对象缓存。现在这个常量只被页面缓存类插件识别,对对象缓存完全无效。别再指望它,装 drop-in 才是正路。

  1. 在服务器上确认 Redis 已运行且 PHP 装了 redis 扩展:redis-cli ping 应返回 PONG 。

  2. 安装 Redis Object Cache 类插件,但先不要急着启用——确认 wp-config.php 中设置了 WP_REDIS_HOST、WP_REDIS_PORT 等常量。

  3. 启用插件的 drop-in 部署动作,它会自动把 object-cache.php 复制到 wp-content 目录。

  4. 用 Query Monitor 对比启用前后的单次请求 SQL 查询数与耗时,确认对象缓存命中。

  5. 把业务里昂贵的 WP_Query 与远程请求改成 wp_cache_get/set,并设置明确的 group 与过期时间。

Transients 的三个真实陷阱

1. 过期时间是「最大值」,不是「最小值」

官方一句话很到位:Transient expiration times are a maximum time. There is no minimum age. 数据库升级、外部对象缓存被清都可能让 transient 提前消失。所以代码必须始终假设 transient 不存在,写成”取不到就重建”,而不是”设了就有”。

2. 不传过期时间=autoload

看源码就明白了:没有 expiration 时,transient 对应的 option 会被标记为 autoload yes,意味着每个页面请求都会把它拉起来,哪怕从不使用。上千条大体积 transient 全部 autoload,等于给每个请求加了固定税。

// 坏:默认 expiration = 0,会被 autoload
set_transient( 'heavy_data', $data );

// 好:哪怕只写 1 秒,也不会进 autoload 集
set_transient( 'heavy_data', $data, 1 );

3. 取回值要用恒等判断

get_transient() 失败返回 false,而你缓存的数据本身也可能是 0 或空字符串。必须用 ===:

if ( false === ( $data = get_transient( 'heavy_data' ) ) ) {
    // 没有或已过期,重建
}
Transient 不会自己消失:它只在「有人访问并触发读取」时才被清理。过期数据会一直躺在数据库里直到被主动请求,所以长期不用的 transient 要配合定时任务清理。

上线前自查

  • wp-content/object-cache.php 是否存在且能被 PHP 读取。
  • Redis 是否设置了最大内存与淘汰策略(maxmemory-policy allkeys-lru)。
  • 是否给业务数据加了自定义 group,避免与核心 group 混用。
  • 是否检查过 wp_cache_supports( 'flush_group' ) 再清理。
  • 所有 set_transient() 是否都传了过期时间。

WordPress 对象缓存默认开启了吗?
开启但不持久。核心自带对象缓存会在单次请求内缓存查询结果,请求结束即释放。要跨请求留存必须安装 object-cache.php drop-in,把后端接到 Redis 或 Memcached 。

已经用了页面缓存插件,还有必要做对象缓存吗?
有必要。页面缓存只覆盖完全静态的匿名访问;登录用户、后台、 REST API 、 AJAX 请求和动态片段都会绕过它,这些场景的压力靠对象缓存承接。

Transients 和 Object Cache 该用哪个?
需要保证「一定被缓存」时用 Transients;只做请求内或可接受丢失的性能优化时用 wp_cache_*。装了持久化后端后,Transients 会自动改用 wp_cache_* 实现,不再落库。

为什么 set_transient 不传过期时间会有性能问题?
因为源码会给无过期时间的 transient 设置 autoload = yes,导致它在每个页面请求时被自动加载。即使数据从不被使用也一样,累积起来会显著拖慢整站。

可以用 Redis 直接替换数据库吗?
不能。对象缓存是缓存层,随时可能被淘汰;持久数据必须落在数据库里。任何把 Redis 当唯一数据源的设计都会在某个缓存清空时丢数据。

相关阅读

本文依据 WordPress 官方开发者文档的 Transients API 与 WP_Object_Cache 章节整理,并结合工程实践重新组织。

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

795文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示