静态缓存不涉及缓存穿透,真正问题分两类:一是静态资源更新后浏览器加载旧文件,需用contenthash+mix()函数确保url唯一;二是数据查询未缓存空结果,应存结构化空标记如['__empty__'=>true]。

ThinkPHP 8.0 的“静态缓存”本身不涉及缓存穿透问题——缓存穿透是数据缓存层(如 Redis、数据库查询结果)面对无效 key 查询时的场景,而静态资源(CSS/JS/图片)走的是 HTTP 缓存或 Web 服务器直供路径,不存在“查数据库为空”的逻辑。
你真正遇到的,大概率是两类混淆问题之一:
- 把静态资源更新后浏览器仍加载旧文件,误称为“缓存穿透”;
- 或在数据查询逻辑中用了缓存,但没处理空结果,这才属于真正的缓存穿透。
下面分清楚场景,直接给解法:
静态资源“不更新”不是穿透,是强缓存没失效
浏览器反复加载旧 CSS/JS,本质是 URL 没变 + 响应头允许长期缓存(如 Cache-Control: public, max-age=31536000),导致根本不去服务端验证。
解决关键:让每次构建产出的静态文件 URL 唯一可变。
- ✅ 推荐用 Laravel Mix 的
.version():
在webpack.mix.js末尾加.version(),运行npm run production后生成带 contenthash 的文件,如app.css?id=7a2b9c; - ✅ Blade 中必须用
{{ mix('css/app.css') }}引入(TP8 不内置该函数,需手动封装读取mix-manifest.json); - ❌ 禁止硬编码
<link href="/static/css/app.css">—— 这样永远无法触发新文件请求。
数据缓存才真有穿透,TP8 必须主动存空标记
比如查用户 User::find(99999) 返回 null,若直接 Cache::get($key) 拿不到就去查库,且不缓存 null,那所有相同请求都会打库。
正确做法是:查不到也要写缓存,但用结构化空标记。
- 查库前先
Cache::get($key); - 若返回
null,再查数据库; - 若数据库也返回空,写入
Cache::set($key, ['__empty__' => true], 60)(60秒足够防刷); - 后续请求命中后,判断是否含
__empty__键,是则直接返回 null,不查库。
注意:别用 false、0、'' 作空值,它们可能是合法业务数据。
Nginx 配置错误会让所有前端缓存方案失效
很多“版本号不管用”,其实是静态文件压根没被 Nginx 正确返回:
- ✅
location ^~ /static/规则必须放在所有\.php$匹配之前; - ✅
alias必须指向public/static(不是root); - ✅ 用
curl -I https://yoursite.com/static/js/app.js检查响应头:-
Content-Type应为application/javascript; -
Cache-Control应存在,且不是no-cache或缺失。
-
如果返回 text/html 或 404,说明请求被 TP 路由接管了,版本号再准也没用。
不复杂但容易忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











