Doctrine结果缓存需手动启用,仅配置result_cache_driver无效;必须在代码中调用useResultCache()或getResult(['cache'=>true]),且依赖cache.adapter.redis_tag_aware支持标签失效,否则缓存无法按业务维度更新。

Doctrine 查询结果缓存必须手动启用
配置文件里开 result_cache_driver 只是铺路,不写代码它不会自动缓存任何查询。很多人配完 YAML 就以为生效了,结果查 100 次还是跑 100 次 SQL。
关键动作在 Repository 或 Service 层调用时加缓存指令:
-
$query->useResultCache(true, 3600, 'movie_actors_list')—— 显式启用,指定 TTL 和缓存键前缀 - 或更推荐:用
getResult(['cache' => true])(Symfony 6.4+)配合全局池名,避免硬编码键名 - 注意:只有
getResult()生效,getScalarResult()或execute()不走结果缓存
cache.adapter.redis_tag_aware 是 Doctrine 结果缓存的刚需
普通 cache.adapter.redis 无法按实体或业务维度批量失效缓存,比如用户修改头像后想清掉“所有含该用户 ID 的缓存”,只能靠标签(tags)实现。
cache.adapter.redis_tag_aware 是唯一支持 PSR-6 标签操作的 Redis 适配器,Doctrine 结果缓存默认依赖它做 invalidateTags():
- 配置中必须设
pool: doctrine.result_cache_pool并指向该适配器 - 若误用
cache.adapter.redis,$em->getCache()->deleteItemByTag()会静默失败,缓存永远不更新 - 没 Redis?可用
cache.adapter.php_files过渡,但并发 >50 QPS 时 IO 成瓶颈,别上生产
多对多查询缓存容易漏掉关联实体变更
比如查「电影的所有演员」用了结果缓存,但演员改名、电影下架、中间表 movie_actor 行被删——这些变更 Doctrine 不会自动触发缓存失效。
必须主动打标签并管理生命周期:
- 查询时用
$query->setCacheTags(['movie_'.$id, 'actor_list']) - 在 Actor 或 Movie 的 Repository 更新方法里,调用
$cachePool->invalidateTags(['movie_'.$id]) - 不要依赖
metadata_cache_driver或query_cache_driver来解决这个问题——它们管的是 DQL 解析和映射元数据,不是业务数据新鲜度
APP_ENV=prod 且 debug=false 才真正启用缓存池
开发环境下即使配了 Redis,cache.app 池也会 fallback 到 cache.adapter.array,内存型、无持久化、不跨请求——看起来“能跑”,实际没效果。
验证是否真实生效:
- 执行
php bin/console debug:container --parameter=kernel.debug确认输出false - 连上 Redis CLI,运行
KEYS "doctrine.result_cache_pool*"看是否有键生成 - 查日志:
grep -i "cache.hit\|cache.miss" var/log/prod.log,命中率低于 30% 要检查 TTL 或标签逻辑


















