根本原因是缓存未真正生效或静态文件被绕过:控制器仍走MVC流程、Web服务器未直发静态文件、伪静态规则未排除.html、缓存路径权限问题、文件缓存过期机制失效、动态内容未分离、并发写锁冲突及缓存键设计不合理。

静态页面生成后,为什么访问还是慢?
根本原因往往是缓存没真正生效,或者静态文件被绕过了。ThinkPHP 的 buildHtml() 生成的 HTML 文件本身不带逻辑,但如果你在控制器里仍调用 display() 或走完整 MVC 流程,那静态文件根本没被读取——CPU 还是在跑模板解析、数据库查询、钩子执行。
- 确认 Nginx/Apache 直接命中静态文件:比如访问
/news/123.html时,Web 服务器应直接返回该文件,不转发给 PHP-FPM;检查日志里有没有PHP-FPM处理这条请求的记录 - 避免「伪静态」干扰:如果用了 URL 重写(如把
/news/123重写成index.php?s=/news/123),必须加条件排除已存在的 .html 文件,否则请求仍进 PHP -
buildHtml()默认生成到./Application/Runtime/Html/,路径不对或权限不足会导致静默失败——生成后务必ls -l看文件是否存在、是否可读
如何让缓存真正「自动失效」而不手动删 Runtime?
ThinkPHP 的 S() 和 cache() 默认用文件缓存,但依赖文件修改时间判断过期,而 Linux 下文件系统(尤其是 ext4)的 atime/mtime 更新有延迟或被挂载选项禁用(如 noatime),导致缓存不刷新。
- 改用
memcached或redis驱动:它们用内存计时,失效精准;配置CACHE_TYPE为redis,并确保REDIS_HOST可连通 - 文件缓存下,别依赖「自动过期」:对内容更新频繁的页面(如首页),用带版本号的缓存键,比如
index_v2,而不是固定写死index - 注意
HTML_CACHE_TIME单位是秒,设成0不代表永不过期,而是「不启用 HTML 缓存」——这是常见误解
开启 HTML 缓存后,用户登录态和动态区块怎么处理?
全页静态化会把 session、cookie、用户昵称、未读消息数等一并固化,下次访问就变成「假登录」。不能靠 JS 异步补全来凑合,因为 SEO 和首屏性能会受损。
- 用
{:widget('common@userbar')}这类标签做「局部动态」:在静态 HTML 中预留占位符,由中间件或 Nginx 的sub_filter在响应输出前替换(需关闭 gzip) - 更稳妥的做法是「动静分离」:首页静态化,用户中心页完全走动态;用
define('HTML_CACHE_ON', false)在特定模块关闭缓存 - 别在静态模板里写
<?php echo session('user_name'); ?>—— 这段代码会被当成纯文本输出,不会执行
为什么开缓存后 CPU 负载反而升高了?
典型诱因是缓存并发写冲突。ThinkPHP 文件缓存默认用 flock 加锁,但高并发下大量请求排队等锁、反复尝试写入同一缓存文件,导致 PHP 进程阻塞、CPU 在空转等待。
立即学习“PHP免费学习笔记(深入)”;
- 检查
Runtime/Cache/下是否有大量*_lock临时文件残留——说明锁没释放,可能是进程异常退出或超时中断 - 把缓存目录移到
/dev/shm(内存文件系统):减少磁盘 IO 压力,chmod 777 /dev/shm/thinkcache并在配置中指定CACHE_PATH - 对热点数据(如导航栏、分类列表),用
cache('nav_list', $data, 3600)主动预热,避免所有请求同时穿透到数据库
最麻烦的其实是缓存键设计——比如用 md5($_SERVER['REQUEST_URI']) 当键,看似简单,但 GET 参数顺序不同(?a=1&b=2 vs ?b=2&a=1)会产生不同键,浪费存储又增加生成压力。实际要用 http_build_query(array_merge(...)) 标准化参数再哈希。



















