文件缓存使ThinkPHP变慢,因磁盘I/O、flock阻塞及ext4目录项锁争用,QPS远低于Redis;应改用Redis并正确配置连接池、前缀、序列化及原子操作。

为什么文件缓存会让ThinkPHP变慢
文件缓存每次读写都要走磁盘I/O,尤其在高并发下,flock、file_get_contents、file_put_contents会排队阻塞。Linux下大量小文件还容易触发ext4的目录项锁争用,实际QPS可能比Redis低一个数量级。
- 场景:用户登录态、配置项、数据库查询结果缓存——这些本该毫秒级返回,但文件缓存常拖到20ms+
- 典型错误现象:
Cache::get('user_123')偶发超时,日志里出现Warning: file_put_contents(): failed to open stream: No space left on device(其实是inode耗尽) - 不是所有缓存都适合搬进Redis:频繁变更且只读一次的数据(如临时验证码),用文件反而更轻量
ThinkPHP 6.x 配置Redis缓存驱动的关键步骤
别直接改config/cache.php里的default为redis就完事——默认Redis连接没开持久化,每次请求新建连接,反而更慢。
- 必须设置
'connection' => 'default',并在config/cache.php的stores.redis里配好'host'、'port'、'password' - 关键参数:
'prefix' => 'tp6:'防止和其他项目冲突;'expire' => 3600比文件缓存的默认3600更可控(文件缓存实际依赖文件mtime,不精确) - 生产环境务必加
'options' => [Redis::OPT_PREFIX => 'tp6:'],避免用setOption动态设——TP6.0.13之前有bug会导致前缀失效
哪些缓存操作必须改写才能真正减少IO
直接把Cache::get()换成Redis后,如果还沿用文件缓存那一套“先查再写”的逻辑,Redis的原子性优势就浪费了。
- 别再手动
if (!Cache::has($key)) { $data = db()->...; Cache::set($key, $data); }——改用Cache::remember($key, $ttl, function() { return db()->...; }) -
Cache::tag('user')->get('profile_123')这种标签缓存,在Redis里实际是靠key前缀模拟的,删除时会扫全量key,慎用;高频更新场景改用Cache::store('redis')->set($key, $value, $ttl)绕过Tag层 - 注意
Cache::pull()在Redis驱动下是GETDEL命令(TP6.1+),但老版本是GET+DEL两步,中间可能被其他进程读到旧值
Redis连接池和序列化踩坑点
ThinkPHP默认用Predis\Client或Redis扩展,但两者对连接复用和序列化的处理差异很大,直接影响性能。
立即学习“PHP免费学习笔记(深入)”;
- 用
ext-redis扩展(推荐):配置'serialize' => 'php'最稳;若用'serialize' => 'json',遇到DateTime对象会报Serialization of 'DateTime' is not allowed - 连接池不是开箱即用:需在
config/cache.php里显式加'pool' => ['min_connections' => 5, 'max_connections' => 20],否则单连接扛不住并发 - Redis服务端
maxmemory-policy别设noeviction——缓存满时Cache::set()会直接失败,错误信息是ERR command not allowed when used memory > 'maxmemory'
Redis不是万能胶水,比如大对象(>1MB)缓存反而增加网络传输开销,这时候该切分数据或用本地缓存兜底。真正省IO的关键,是让Redis只干它最擅长的事:快读快写、带过期、支持原子操作。



















