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 与数据库 |
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 Cache | Redis 服务 + PHP 扩展 | 单机 / 主从,最主流 |
| Memcached | memcached 服务 + 扩展 | 纯内存场景,多机水平扩展 |
| SQLite Object Cache | SQLite3 扩展 | 共享主机、无 Redis 环境 |
| Docket Cache | PHP OPcache | 轻量方案,省一个外部服务 |
一个历史坑:WP_CACHE 常量(展开)
wp-config.php 里写 define( 'WP_CACHE', true ); 就能持久化对象缓存。现在这个常量只被页面缓存类插件识别,对对象缓存完全无效。别再指望它,装 drop-in 才是正路。- 在服务器上确认 Redis 已运行且 PHP 装了 redis 扩展:
redis-cli ping应返回 PONG 。 - 安装 Redis Object Cache 类插件,但先不要急着启用——确认 wp-config.php 中设置了
WP_REDIS_HOST、WP_REDIS_PORT等常量。 - 启用插件的 drop-in 部署动作,它会自动把
object-cache.php复制到 wp-content 目录。 - 用 Query Monitor 对比启用前后的单次请求 SQL 查询数与耗时,确认对象缓存命中。
- 把业务里昂贵的 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' ) ) ) {
// 没有或已过期,重建
}
上线前自查
wp-content/object-cache.php是否存在且能被 PHP 读取。- Redis 是否设置了最大内存与淘汰策略(
maxmemory-policy allkeys-lru)。 - 是否给业务数据加了自定义 group,避免与核心 group 混用。
- 是否检查过
wp_cache_supports( 'flush_group' )再清理。 - 所有
set_transient()是否都传了过期时间。
WordPress 对象缓存默认开启了吗?
已经用了页面缓存插件,还有必要做对象缓存吗?
Transients 和 Object Cache 该用哪个?
为什么 set_transient 不传过期时间会有性能问题?
可以用 Redis 直接替换数据库吗?
相关阅读
- WordPress 数据库优化:wp_options 、过期 transient 与修订版本清理实操
- WordPress 速度优化实测:从 3 秒到 0.5 秒的 8 个动作
- WordPress 定时任务(WP-Cron)怎么用:调度注册、时区坑与系统 cron 接管
本文依据 WordPress 官方开发者文档的 Transients API 与 WP_Object_Cache 章节整理,并结合工程实践重新组织。








评论 (0)