tp5.1 file缓存高并发写入失败本质是多进程争抢文件/目录且缺乏原子性保护,需确保runtime/cache及其子目录属主统一(如www)、key规范化、禁用自动过期并主动清理,或直接切换为redis驱动。

TP5.1 使用 File 驱动开启缓存后,在高并发场景下出现写入报错(如 failed to open stream: Permission denied、file_put_contents(): Only 0 of X bytes written 或静默失败),本质不是“并发太多”,而是 File 驱动在多进程/多线程争抢同一文件或目录时缺乏原子性保护,加上权限与路径一致性没对齐。
确认 runtime/cache 目录结构与权限是否真正就绪
File 驱动会按缓存 key 的 MD5 前两位自动创建子目录(如 runtime/cache/fa/、runtime/cache/b2/),每个子目录都需满足:
- 父目录
runtime/cache/必须存在且可写(chmod 755或更宽松) - 所有自动生成的子目录(如
fa/、b2/)所有者必须与 PHP-FPM 进程用户完全一致(例如都是www或www-data) - 不能由不同用户(如 root 写过一次,之后 www 尝试覆盖)混用,否则子目录继承错误属主,后续写入被拒
避免 key 冲突与高频覆盖引发的锁竞争
TP5.1 的 File 驱动默认对每个缓存项单独写一个文件,若多个请求同时写入相同 key(比如共用 config:site),会触发系统级文件锁争抢。更常见的是 key 设计不当导致“伪高频”:
- 前端带时间戳、随机参数拼 key(如
user:123?t=1726595520),每次生成新 key,缓存文件数量爆炸,磁盘 inode 耗尽或遍历变慢 - 未过滤空格、大小写、URL 编码字符,使本应相同的 key 被当成多个 key 处理
- 建议 key 统一转小写、trim 空格、移除无意义参数,用
md5(serialize($params))替代字符串拼接
禁用自动过期,改为主动清理 + 合理设置有效期
File 驱动依赖文件修改时间(mtime)判断过期,但 Linux 下 mtime 在 NFS 或某些容器环境更新不及时;同时,过期扫描(think\cache\driver\File::clear())本身是全目录遍历,高并发时易卡住或漏删。
- 在
config/cache.php中设'default' => ['expire' => 3600],避免设为0(永不过期) - 关键数据更新后,立刻调用
Cache::rm('your_key'),不要依赖自动失效 - 禁用定时自动清理(如 Laravel 的 schedule),TP5.1 没内置该机制,自行实现容易引入竞态
临时缓解:降级为 Redis 驱动(推荐)
File 驱动天生不适合并发写入场景。若已部署 Redis,切换成本极低:
- 确保
php-redis扩展启用(php -m | grep redis) - 在
config/cache.php中将'default'改为'redis',并配置'redis' => [...]区块 - Redis 单节点写入是原子操作,天然规避文件锁问题,且性能提升明显
不复杂但容易忽略:File 驱动的并发问题,往往不是代码错了,而是目录属主没对齐、key 没规范、清理逻辑没接管——先清掉 runtime/cache 全部内容,再按上述四步重配,多数能立刻恢复。











