不能直接 new cacheinterface,需用 symfony 缓存池(如 arrayadapter/filesystemadapter)或容器注入的 cache.app 服务;键名须规范、值须可序列化;页面片段缓存应存渲染后的字符串而非 response。

CacheInterface 怎么用才不踩空指针
直接 new 一个 CacheInterface 实例?不行——它是接口,不能实例化。实际得靠 Symfony 的缓存池(如 ArrayAdapter、FilesystemAdapter)或容器自动注入的预配置服务(比如 cache.app)。
常见错误是手动 new PhpFilesAdapter 却忘了传 $directory,结果抛出 InvalidArgumentException:“The directory "" does not exist.”
- 开发环境优先用
ArrayAdapter(内存缓存,无持久化,调试友好) - 生产环境用
FilesystemAdapter要确保$directory可写,路径别写相对路径,用%kernel.cache_dir%/pools这类参数 - 如果从容器拿服务,直接类型提示
CacheInterface或用$this->get('cache.app'),别自己 new
set() 和 get() 的键名和序列化陷阱
缓存键不是随便拼的字符串。Symfony 默认对键做标准化(转小写、去空白、限制长度),但更关键的是:值必须可序列化。传 Closure、Resource、Doctrine Proxy 对象进去会静默失败或报 SerializationException。
典型翻车场景:缓存一个带未初始化关联关系的 User 实体,后续 get() 反序列化后调用 $user->getPosts() 报错“Entity not managed”。
- 只缓存数组、标量、stdClass 或明确实现
__serialize/__unserialize的 DTO - 键名避免含特殊字符,推荐用冒号分隔层级,如
user:profile:123,别用user/profile/123(部分适配器会截断) -
set()第二个参数是值,第三个是 TTL(秒数),不传默认永不过期;传null表示用适配器默认 TTL
怎么缓存页面片段而不是整个响应
Symfony Cache 组件本身不处理 HTTP 响应缓存,它只管数据。所谓“页面缓存”,其实是把渲染结果(字符串)当普通值存起来,再在响应前手动读取并输出——这叫“片段缓存”,不是代理层缓存。
容易混淆的是:有人试图用 Response 对象直接塞进 set(),结果反序列化失败,因为 Response 不可序列化。
- 正确做法:先
$content = $this->renderView('template.html.twig', $data),再$cache->set($key, $content, 300) - 注意 Twig 模板里如果有动态内容(如当前时间、用户 ID),必须在渲染前就传入,缓存的是最终 HTML 字符串
- 不要用这个代替 HTTP 缓存头;需要浏览器或 CDN 缓存,请用
Response::setPublic()+ ETag/Last-Modified
FilesystemAdapter 在高并发下为什么变慢甚至卡住
文件缓存看着简单,但在高并发写入时,多个进程争抢同一个缓存文件的 flock,会排队阻塞。更糟的是,FilesystemAdapter 默认每 100 次写入就执行一次 prune()(清理过期项),而 prune() 是全目录扫描,IO 开销大。
线上看到 CPU 突升、响应延迟跳到几百毫秒,大概率是这个原因。
- 改用
RedisAdapter或PdoAdapter,它们的原子操作和索引机制天然适合并发 - 如果非用文件,至少关掉自动 prune:
new FilesystemAdapter('', 0, '/path/to/cache')(第三个参数设为 0),再单独起 cron 清理 - 别把缓存目录放在 NFS 或网络盘上——flock 在分布式文件系统上不可靠,可能失效
缓存键的设计和存储后端的选择,比怎么调用 get() 和 set() 更影响实际效果。很多人调通了 API 就以为完事,结果上线后发现缓存没生效,或者生效了却拖慢整体性能——问题往往出在适配器行为和键值生命周期的隐含约定上。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











