内容提要
文章以去不图床(Laravel全栈)为例,介绍如何将外链首字节从800ms降至60ms。核心思路是写路径异步化、读路径做薄:使用PHP 8.2+并开启OPcache、调大max_accelerated_files,执行config/route/view缓存;FPM按IO密集型配置进程池;用Redis缓存角色组、存储策略等热点数据;上传走队列,图片存NVMe并由Nginx直出;最后用FastCGI缓存公开页面,登录态、API、原图保护路由必须bypass且no_cache,并开启cache_lock防击穿。
延伸解读
先定位瓶颈,再决定是否上缓存
文章强调优化前需用三个指标判断瓶颈:curl 测 TTFB、FPM 状态看 listen queue、Laravel 慢日志。若 listen queue 长期大于 0,说明进程数不足;若 TTFB 高但 total 接近,则瓶颈在 PHP 之前,此时 FastCGI 缓存才对症。盲目改配置可能掩盖真正问题,先测量再动手是全文的方法论前提。
bypass 与 no_cache 必须成对写
文章指出只写 fastcgi_cache_bypass 是最常见错误:登录用户虽不读缓存,但其私有页面仍会被写入缓存,下一个匿名访客直接命中,构成数据泄露而非性能问题。因此登录态、后台、API、原图保护路由必须同时设置 bypass 和 no_cache,并同时检查 Cookie 与 Authorization 头,宁可多排除也不能少。
原图保护与 FastCGI 缓存存在架构冲突
开启原图保护后,图片请求由 PHP 输出而非 Nginx 直出,若被当作页面缓存,缓存中会存入某个用户的私有图片内容,对私有相册是严重泄露。文章建议从架构上隔开:公共图片走 /uploads/ 由 Nginx 加 CDN 处理,私有图片走独立 PHP location 并强制关闭缓存,两条路径互不干扰。
缓存的真正价值是削峰而非单点毫秒
文章对比数据显示,FPM 峰值进程从 32 打满降至 14,意味着同样机器可扛 2 到 3 倍访问量。同时提醒对比要看 P99 而非平均值,用户抱怨的往往是那 3 秒。压测应分匿名列表、登录控制台、图片直出三个场景,才能定位哪一层仍有空间,避免被平均 TTFB 误导。
Q&A
图床这类业务为什么特别适合用 FastCGI 缓存来加速?
图床业务写请求少、读请求极多,且公开页面(首页、图片广场、公开相册等)内容几乎不变、匿名可访问占比高,输出对所有访客一致。FastCGI 缓存将渲染好的 HTML 存到磁盘,命中时 Nginx 直接返回,不碰 PHP 和数据库,因此能大幅降低读路径耗时。
配置 FastCGI 缓存时,fastcgi_cache_bypass 和 fastcgi_no_cache 有什么区别?为什么两个都要写?
bypass 是读缓存前判断条件,成立则跳过读缓存但仍转给 PHP;no_cache 是条件成立时不把本次响应写入缓存。只写 bypass 会导致登录用户的私有页面被存入缓存,下一个匿名访客可能命中,造成数据泄露。因此登录态和后台必须两个都写。
图床的 FPM 进程池 pm.max_children 应该怎么设置?为什么不能盲目调大?
图床是 IO 密集型,pm.max_children 建议设为核数的 2 倍左右(如 16 核设 32)。不能盲目调大是因为 Imagick 处理大图时单个进程可能占用几百 MB 内存,32 个并发满负荷可能打满内存导致 502 或 FPM 重启。
开启原图保护功能后,为什么必须把相关图片请求排除在 FastCGI 缓存之外?
原图保护开启后图片请求由 PHP 处理输出,而 FastCGI 缓存针对 .php 页面请求。若图片请求被缓存,缓存中会存储某个用户的私有图片内容,可能被其他访客命中,造成严重数据泄露。因此必须对原图保护路由设置 bypass 和 no_cache,或从架构上隔离公共图片与私有图片路径。
如何验证 FastCGI 缓存是否真的生效?
在 Nginx 配置中添加 add_header X-Cache-Status $upstream_cache_status always;,然后用 curl -sI 请求页面查看响应头:第一次通常是 MISS,第二次应为 HIT。也可通过 nginx stub_status 观察 HIT/MISS 比例,MISS 过高说明缓存规则太严或页面太动态。
图床优化中,上传链路和访问链路分别做了哪些关键优化?
上传链路(写路径)通过队列异步化(拆分 thumbnails/images 队列)、OPcache、多进程、WebP 压缩等降低耗时;访问链路(读路径)通过 CDN、Nginx 直出图片、FastCGI 缓存公开页面来降低耗时。核心原则是把重活挪到写路径,把读路径做薄。
升级图床程序后,为什么必须清理 OPcache 和 FastCGI 缓存?标准操作步骤是什么?
升级后 config:cache、route:cache 等会变化,但 OPcache 和 FastCGI 缓存仍保留旧内容,导致后台显示新版本但页面还是旧模板。标准操作:执行 php artisan optimize:clear、删除 FastCGI 缓存目录(如 rm -rf /data/nvme/nginx-cache/*)、重启 Supervisor 队列、reload PHP-FPM。