Symfony的Twig与Doctrine缓存短时间自动清除原因排查
Symfony缓存未到配置TTL提前失效问题排查
问题场景
我尝试通过在模板中使用cache标签、在Repository层配置Doctrine缓存的方式降低Symfony网页加载耗时,具体配置如下:
Twig模板缓存配置
{% cache "navbar;v1;" ~ last_updated_at %} {# loops, conditions and HTML ...#} {% endcache %}
Repository层Doctrine结果缓存配置
... return $this->createQueryBuilder('p') ->complexQuery... ->getQuery() ->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true) ->useResultCache(true, 14400) ->getResult();
异常现象
- 首次加载页面耗时很高,产生大量SQL查询:


- 首次加载后立刻刷新页面,加载速度明显提升:


- 保持标签页打开,离开15分钟左右返回刷新,页面加载耗时回到首次加载的水平,缓存疑似被提前清除,此时距离配置的14400秒(4小时)TTL还有很久,相关业务数据也没有做更新操作。
问题原因与修复方案
按出现概率从高到低排序,导致该问题的原因有三类:
- 默认文件缓存被PHP垃圾回收机制误删(概率90%)
未显式配置缓存驱动时,Symfony dev环境默认使用文件系统缓存,存储路径在项目var/cache/目录下。绝大多数默认PHP环境的session.gc_maxlifetime参数值为1440秒(恰好15分钟),PHP会话垃圾回收触发时,会扫描目录下所有超过15分钟未访问的文件,直接判定为过期删除,完全不会读取代码中设置的14400秒TTL参数。 - 缓存未绑定独立池,被全局策略提前淘汰
仅在查询中调用useResultCache(true, 14400)不会强制使用你设置的TTL,如果doctrine.yaml中没有为结果缓存指定独立缓存池,Doctrine会默认使用框架全局的cache.app缓存池。如果该池配置了15分钟的默认生命周期,或是使用Redis作为缓存时配置了allkeys-lru淘汰策略,当缓存内存达到阈值时,15分钟未访问的key会被优先淘汰,不受自身TTL设置限制。 - Twig缓存键生成逻辑异常
Twig缓存键拼接了last_updated_at变量,如果该变量存储在session中、或是取值逻辑存在缺陷,15分钟后刷新时生成的缓存键和之前不一致,会直接绕过原有缓存生成新副本,表现和缓存失效完全一致。可以在缓存标签外层记录每次生成的缓存键值,两次刷新对比即可快速排除该问题。
修复步骤
- 为Doctrine结果缓存、Twig片段缓存配置独立的Redis缓存驱动,禁止使用默认文件缓存做长期缓存:
# config/packages/doctrine.yaml doctrine: orm: result_cache_driver: type: redis host: 127.0.0.1 port: 6379 pool: cache.doctrine_result# config/packages/cache.yaml framework: cache: pools: cache.doctrine_result: adapter: cache.adapter.redis default_lifetime: 14400 cache.twig_fragment: adapter: cache.adapter.redis default_lifetime: 3600 - Twig cache标签显式指定使用独立缓存池,避免受全局缓存配置影响:
{% cache "navbar;v1;" ~ last_updated_at with cache.twig_fragment %} {# loops, conditions and HTML ...#} {% endcache %} - 调整Redis缓存实例的淘汰策略为
volatile-ttl,禁止使用allkeys-lru策略,避免未过期的缓存key被提前淘汰。 - 若暂时无法使用Redis,需将文件缓存的存储路径调整到
var/cache/以外的独立目录,同时调大PHP的session.gc_maxlifetime参数,避免垃圾回收误删缓存文件。
内容的提问来源于stack exchange,提问作者Sami
相关产品推荐
相关产品推荐

