结论是环境、适配器、标签支持、注入方式四者必须咬合:APP_ENV=prod和opcache需先到位,cache.app必须用TagAwareAdapter嵌套RedisAdapter,禁用手动new,HTTP缓存需单独配置,键命名空间与连接池复用不可忽略。

RedisAdapter,90% 情况下缓存根本不会按预期工作。
APP_ENV=prod 和 opcache 必须先到位
缓存失效的第一大原因永远是环境没切对,不是配置写错了。PHP 8.2 下 Symfony 7/8 默认禁用调试模式缓存,但很多人只改了 .env,却忘了 Web 服务器(Nginx/Apache)或 CLI 环境里 APP_ENV 仍为 dev:
• 运行 php -r "echo $_ENV['APP_ENV'] ?? 'not set';" 验证是否真为 prod
• 检查 opcache.enable=1 和 opcache.enable_cli=1(部署脚本预热依赖它)
• bin/console debug:container cache.app 返回的类名必须含 TagAwareAdapter 或 RedisAdapter,而不是 ArrayAdapter —— 后者是 dev 模式 fallback 行为
cache.app 不要直接配 RedisAdapter,必须用 tag-aware
生产环境需要按标签清除缓存(比如清掉所有“user_profile”相关条目),而cache.adapter.redis 本身不支持标签。
YAML 配置里别这么写:cache.app: cache.adapter.redis
正确做法是两层嵌套:
• 底层用 cache.adapter.redis
• 外层套 cache.adapter.redis_tag_aware
示例(config/packages/cache.yaml):
framework:<br> cache:<br> app: cache.adapter.redis_tag_aware<br> default_redis_provider: '%env(REDIS_DSN)%'<br> pools:<br> my_api_pool:<br> adapter: cache.adapter.redis_tag_aware<br> default_lifetime: 300
注意:
default_redis_provider 必须带协议(redis://),否则抛 InvalidArgumentException
别在控制器里手动 new Adapter,注入 cache.app 就够了
手写new RedisAdapter() 是最常被复制粘贴的错误操作,会导致连接未复用、TTL 丢失、标签失效。
正确用法只有两种:
• 在服务中通过构造函数注入 Psr\Cache\CacheItemPoolInterface(自动绑定到 cache.app)
• 或在控制器里用 $this->get('cache.app')(Symfony 6.4+ 推荐用属性注入)
典型错误:
• 在 __construct() 里调用 RedisAdapter::createConnection() —— 连接对象无法被容器管理
• 混用 TagAwareAdapter::invalidateTags() 和底层 RedisAdapter::save() —— 标签根本没写进去,清除无效
• 把 cache.app 注入进一个高频调用的服务,但没设为 public: false,导致每次请求都重建实例
HTTP 缓存头和反向代理必须分开配,不能指望 cache.app 替你干这个
cache.app 管的是 PHP 层数据缓存,不是 HTTP 响应缓存。很多团队配好了 Redis 却发现浏览器还是每秒重请求,就是因为混淆了这两层。
关键点:
• 控制器返回 Response 时,必须在读取会话前调用 $response->setPublic()->setMaxAge(300),否则会话中间件会把它覆盖成 private
• #[Cache] 属性只生成响应头,不减少 PHP 执行 —— 要真正跳过控制器逻辑,得靠 Nginx 的 proxy_cache 或 Varnish
• Vary: Authorization,Cookie 头如果没控制好,会导致 CDN 缓存爆炸(每个用户 Cookie 都生成一份副本)
立即学习“PHP免费学习笔记(深入)”;
真正容易被忽略的点是:缓存键命名空间和连接池复用。比如多个池共用同一个default_redis_provider 但没设 namespace,不同业务的缓存项会互相覆盖;又或者在 CLI 命令里用 cache.app,但没意识到 CLI 下 apc.enabled 默认关闭,导致退化成文件缓存。这些细节不显眼,但上线后一压测就暴露。



















