推荐使用 ./yii cache/flush-all 清理缓存,它安全可控,触发各组件自身清理逻辑并兼容锁机制;手动删 @runtime 缓存文件易引发并发异常、锁损坏及文件残留。

直接删 @runtime 下的缓存文件不推荐——容易删错、漏删、破坏锁机制,甚至引发并发写入异常。 正确做法是走框架内置清理路径,或严格限定范围 + 确保进程无句柄占用。
用 cache/flush-all 命令清所有缓存组件
这是最安全、最可控的方式,会触发每个缓存组件自身的清理逻辑(比如 FileCache::gc()、RedisCache::flush()),同时兼容依赖项和锁机制。
- 执行
./yii cache/flush-all,它会列出所有注册的缓存组件并逐个清理 - 若只想清某一个(如
fileCache),用./yii cache/flush fileCache - 该命令不会碰
logs/或assets/,只处理明确声明为cache类型的组件 - 注意:如果缓存组件配置了自定义
cachePath(如指向/tmp/yii-cache),命令仍能正确识别并清理,前提是组件在components中注册且可访问
手动清理 FileCache 目录时必须调用 gc()
直接 rm -rf 缓存目录会导致未过期文件残留、锁文件损坏、甚至后续 get() 失败(因找不到对应 .lock 文件)。
- 正确做法是让
FileCache实例自己清理:$cache = Yii::$app->cache; // 假设是 FileCache 实例 $cache->gc(); // 强制执行垃圾回收:删过期文件 + 清理 lock 文件
- 如果缓存实例没配在
components里,得手动 new 并设好cachePath,否则gc()不知道清哪 -
gc()默认只清理「过期」文件;如要强制清空整个目录(慎用),得先FileHelper::removeDirectory($cache->cachePath)再FileHelper::createDirectory($cache->cachePath) - 别在 Web 请求中频繁调用
gc(),它可能扫描大量文件,阻塞请求
删 @runtime/logs 后磁盘空间不释放?查 lsof | grep deleted
日志文件被删但空间没回来,99% 是 PHP 进程还拿着文件句柄——常见于常驻进程(queue worker、event-sourcing listener、xdebug 调试中的长脚本)。
- 运行
lsof | grep deleted | grep php,看有没有类似php 12345 user 10w REG 8,1 1073741824 123456 /path/to/runtime/logs/app.log (deleted)的输出 - 确认 PID 后,检查该进程用途:
ps aux | grep 12345;如果是队列 worker,重启它:./yii queue/listen --stop && ./yii queue/listen - 临时缓解可用
logrotate配合copytruncate,但治标不治本;长期应确保日志写入器支持 reopen(如 Monolog 的RotatingFileHandler) - 别依赖
touch runtime/logs/.keep来“防止误删”——它对句柄占用毫无作用
别混用 TagDependency::invalidate() 和物理删除
用缓存标签(如用户资料相关缓存)时,清除动作必须走 TagDependency::invalidate(),而不是去删缓存文件或 Redis key。
- 删文件只会让下次
get()返回 null,但旧缓存内容还在磁盘上,且TagDependency标记键(如yii:tag:user_profile)仍有效,导致新数据无法写入 - 正确清除姿势:
\yii\caching\TagDependency::invalidate(Yii::$app->cache, 'user_profile'),这会让所有绑定该 tag 的缓存项在下次get()时自动失效并重建 - 如果缓存组件是 Redis,
invalidate()会删掉标记键;如果是文件缓存,它只删标记文件(.tag.user_profile),不碰数据文件本身 - 多环境部署时,确保
cache组件名、前缀(keyPrefix)一致,否则invalidate()在 dev 环境调用,prod 的标记键根本删不到
真正麻烦的从来不是“怎么删”,而是删完之后缓存组件是否还持有旧状态、依赖是否断裂、句柄是否残留——这些点不验证,清缓存就等于埋雷。











