多级缓存需显式用 cache.adapter.chain 组装,Redis 必须排第一且新建专用池,Filesystem 需独立配置写权限与 filesystem 包,过期时间须用整数秒,获取实例应直接注入 chainCache 服务。

多级缓存不是“自动叠加”,必须显式用 cache.adapter.chain 组装,否则 Redis 和 Filesystem 是互斥的两个池,不会 fallback。
用 cache.adapter.chain 显式声明链式结构
ChainAdapter 不是配置开关,而是一个独立适配器类型,需在 cache.yaml 中明确定义其层级顺序:先查快的(Redis),未命中再查慢的(Filesystem)。
- 顺序决定行为:
adapters列表从左到右即查询优先级,RedisAdapter必须排第一 - 不能复用已有池名,得新建专用池,例如
cache.app.chain - 每个 adapter 都要完整配置 provider 或参数,不能只写别名
示例配置:
framework:
cache:
pools:
cache.app.chain:
adapter: cache.adapter.chain
providers: ['cache.adapter.redis', 'cache.adapter.filesystem']
default_redis_provider: 'redis://localhost'
app: cache.app.chain
FilesystemAdapter 必须可写且已安装 filesystem 包
ChainAdapter 里嵌入的 cache.adapter.filesystem 不会继承全局文件缓存配置——它走的是独立实例路径,所以必须确保:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
symfony/filesystem已通过composer require symfony/filesystem安装,否则报Class not found -
cache.dir显式指定且目录存在、PHP 进程有写权限,例如:cache.adapter.filesystem的provider应设为'%kernel.cache_dir%/pool' - 开发环境默认禁用文件缓存,Chain 中的 Filesystem 才不会“静默失效”
RedisAdapter 过期时间会被截断为整秒
ChainAdapter 本身不干预过期逻辑,但底层 Redis 实现会把 expiresAfter(3600.5) 截成 3600 秒——这在多级链中尤其危险:如果 Redis 层因精度丢失提前失效,请求会穿透到 Filesystem 层,导致缓存雪崩风险升高。
- 务必统一用整数秒调用
expiresAfter(),避免传浮点或DateTime字符串 - 测试时用
$item->getExpiresAt()看实际值,别信代码里写的数字 - Filesystem 层的过期检查是同步扫描,大量过期文件会拖慢首次 fallback 性能
获取缓存实例时必须用池名,不能用通用别名
ChainAdapter 创建的是新池,cache.app 默认指向它,但 DI 容器里 cache.app 服务可能仍被其他配置覆盖。最稳的方式是直接注入具体池服务:
- 在控制器或服务中声明依赖:
private CacheInterface $chainCache; - 对应服务定义里明确绑定:
cache.app.chain→$chainCache - 不要依赖
cache.default或cache.app别名,它们容易被 framework 默认配置劫持
ChainAdapter 的 fallback 是透明的,但它的性能瓶颈往往藏在 Filesystem 层的磁盘 I/O 和权限校验上——这点比 Redis 连接失败更难排查。

















