网站缓存预热实践:发布高峰前让页面提前就绪

网站缓存预热封面图

每次发布新文章或批量更新商品页之后的一个小时,往往是网站全天最慢的时候:缓存刚被清空,第一批访客的每个请求都要回源执行 PHP、查询数据库,首字节等待时间从平时的几十毫秒飙升到一两秒。如果这时正好赶上活动推广、社交平台引流,用户拿到的就是”最贵的那一次访问”。缓存预热(cache warmup)解决的就是这个问题:在真实流量到来之前,让爬虫脚本先把页面逐个访问一遍,把缓存重新填满。这篇文章教你如何搭建一套可复制的预热流程,覆盖 WordPress 与 Nginx 两层缓存,并给出预热效果的验证方法,帮助站点在流量高峰前让页面提前就绪。

先弄清代价:一次缓存未命中到底有多慢

预热值不值得做,先要看未命中与命中的差距。我们用一个内容规模约 800 页的 WordPress(流行的开源建站系统)站点做过对比:在 2 核 VPS虚拟专用服务器)上,命中页面缓存时 TTFB(首字节时间,衡量服务器响应速度的核心指标)稳定在 40-60 毫秒;缓存失效后回源执行 PHP 并查询数据库,TTFB 直接涨到 900-1300 毫秒,相差约 20 倍。若前面还有一层 CDN(内容分发网络)回源,叠加冷连接建立,差距还会进一步拉大。

这个差距在低峰期无所谓,但在两个场景会集中爆发:一是发布后清缓存,二是流量高峰(整点开抢、社交爆款、邮件群发)正好撞上冷缓存。前者站长可以自己控制节奏,后者只能靠提前准备。算一笔账:一个 2000 页的站点,回源单页 1 秒左右,逐页串行预热约需 33 分钟,并发 8 路则能压缩到 5 分钟内——也就是说,只要预热脚本能在高峰前 10 分钟跑完,就能把”高峰期 20 倍的响应差距”基本抹平。理解了这个数量级,后面的配置才有取舍依据。

冷缓存为什么会出现,也值得先想清楚。除了主动清缓存,还有三类常见触发点:缓存插件按过期时间自动淘汰(比如页面缓存设了 1 小时 TTL(存活时间))、凌晨流量低谷期整批条目到期无人补、以及服务器重启或 PHP-FPM 重启连带缓存失效。把它们列出来,你就能推断出站点每天固定的”冷缓存窗口”,预热任务的排期自然就有了依据。

WordPress 层:用 WP-CLI 搭建定时预热

WordPress 站点的预热核心是 WP-CLI(WordPress 的命令行管理工具)。思路很直接:用 wp post list 拿到全部公开页面 URL,再逐个请求一遍。一个可直接保存为 /root/scripts/warmup.sh 的脚本:

#!/bin/bash
wp post list --post_type=post,page --post_status=publish \
  --fields=ID --posts_per_page=3000 --perm=/posts/ \
  | tail -n +2 | while read id; do
      url=$(wp post url "$id" --quiet)
      curl -fsS -o /dev/null -m 20 "$url" &
      while [ "$(jobs -r | wc -l)" -ge 8 ]; do sleep 0.2; done
    done
wait
echo "$(date '+%F %T') warmup done"

脚本里有两处关键参数。--posts_per_page=3000 决定预热覆盖面:如果站点文章超过 3000 篇,只预最近 3000 篇即可,长尾旧文流量占比通常不到一成,全部预热收益很低。后台并发数 8(通过 jobs 控制)决定预热速度,同时保护数据库:并发过高会让 PHP-FPM worker 全被预热请求占满,真实用户反而排队,2 核机器建议 4-8,4 核可以到 16。

把脚本挂到 cron(Linux 的定时任务服务),排期要避开两个时间:一是整点流量高峰本身,二是缓存自动过期的时间点。比如页面缓存 TTL 是 1 小时,那么每小时第 5 分钟跑一次 curl 预热,就能保证整批条目刚到期就被补上;凌晨低谷期可以加一次全量预热,把每天最早的高峰也覆盖住。crontab -e 加一行 5 * * * * /root/scripts/warmup.sh >> /var/log/warmup.log 2>&1 即可,日志保留下来,后文验证命中率时要用。如果你不熟悉 WP-CLI 的安装,可以先从WordPress 教程专区了解服务器侧的基础操作,再回来配置脚本。

如果用的是缓存插件,还要注意预热顺序:预热必须在”缓存已清理”之后执行,否则爬到的是旧页面。常见做法是把清理和预热写进同一个脚本顺序执行,或者用插件自己的钩子——例如部分缓存插件在清空缓存后会触发 w3tc_flush_all 或类似动作,挂一个回调启动预热脚本,就能实现”发布后自动回填”,这也是发布触发式预热最省心的实现方式。

缓存预热触发方式对比图

Nginx 层:服务器端缓存的预热与排期

WordPress 插件的页面缓存之外,很多性能要求更高的站点会在 Nginx(高性能 Web 服务器)层再做一次 fastcgi_cache。这一层的预热逻辑相同,但有两个细节要单独处理。第一是 Host 头:直接用 curl http://127.0.0.1/ 打本机不会命中正确的缓存 key,必须带 -H "Host: 你的域名",或者干脆用 --resolve 你的域名:443:127.0.0.1 走本地回环访问 HTTPS 站点:

curl -fsS -o /dev/null -m 20 --resolve "example.com:443:127.0.0.1" https://example.com/

第二是缓存 key 的构成。fastcgi_cache 的 key 通常包含 scheme、host、request_uri,个别配置还会加入 cookie 或移动端标识。预热脚本必须按”与真实访客完全一致的请求形态”去请求页面,否则生成的缓存条目 key 对不上,等于白做。检查方法是先在 Nginx 配置里确认 fastcgi_cache_key 这一行,再对照预热命令补齐对应的头。

服务器端预热还有个天然优势:可以直接绕过外部 CDN 单独预热源站。做法是在 Nginx 加一段仅限内网 IP 访问的 location,预热脚本请求这个入口,生成的缓存与线上完全共用,但流量不过 CDN,避免预热请求把 CDN 的请求配额或带宽(网络传输能力)消耗掉。活动大促前,建议把预热提前量排得更宽:提前 30 分钟启动全量预热,预留一次失败重跑的时间,比卡着开抢前 5 分钟才开始稳妥得多。关于源站响应速度的优化,还可以配合TTFB 主机层优化指南一起做,预热解决”冷启动”,主机层决定”回源时的下限”。

排期层面,把”定时预热”和”发布触发”组合起来最省心:低峰期(比如凌晨 4 点)做一次全量,每小时整点后第 5 分钟做一次增量(只预最近更新的内容),发布动作清缓存后立即触发一次针对新页面及其相邻页(首页、分类页、相关文章)的小范围预热。三层叠加后,站点基本不存在成规模的冷缓存窗口。

验证效果:用命中率与 TTFB 说话

预热做完不能只看脚本退出码,要用两个指标验证。第一个是缓存命中率:Nginx 侧在配置里加 add_header X-Cache-Status $upstream_cache_status;,然后用循环抽查:

for i in $(seq 1 20); do
  curl -s -o /dev/null -D - --resolve "example.com:443:127.0.0.1" \
    https://example.com/ | grep -i x-cache-status
done | sort | uniq -c

输出里 HIT 占比就是当前命中率。预热刚跑完时抽查应接近全 HIT;如果预热后仍有大量 MISS,说明预热请求的 key 与真实请求不一致,回去检查 Host 头与缓存 key。第二个指标是 TTFB:用 curl -w "%{time_starttransfer}\n" -o /dev/null -s https://example.com/ 连续采样 10 次取中位数,预热后应稳定在几十毫秒量级,而不是每次都在几百毫秒以上波动。

日志层也能给出证据:把 /var/log/warmup.log 里的完成时间与监控图的 TTFB 曲线叠在一起看,理想状态是”缓存清理事件之后,TTFB 不再出现持续 10 分钟以上的高位平台期”。如果活动当天要快速复核,可以直接看服务器运维专栏里介绍的服务器监控基础,把缓存命中率纳入日常巡检面板。验证通过后,预热脚本本身也要纳入维护:站点改版导致 URL 结构变化时,脚本里的 URL 来源逻辑要同步更新,否则预热会一直悄悄访问 404 页面,制造”预热正常”的假象。

缓存命中率验证示意图

写在最后

缓存预热不是新概念,但它恰好处在”插件默认不管、出事时才想起”的尴尬位置。总结这套实践的核心:先用数据确认未命中的真实代价,再用 WP-CLI 脚本覆盖 WordPress 层、用带正确 Host 与 key 的 curl 覆盖 Nginx 层,最后用命中率与 TTFB 抽查闭环验证。定时、增量、发布触发三层叠加,站点就能在流量高峰到来之前把页面提前就绪。

给你的下一步行动建议:今晚就用 wp post list --post_type=post --post_status=publish --field=ID | wc -l 数一下自己的页面规模,复制上文脚本先跑一次手动预热,再用 20 次 curl 抽查命中率——整个过程不超过 20 分钟,却能直接决定下次大促时用户打开的是 50 毫秒还是 1.5 秒。如果你的站点即将迎来持续的高峰流量,可以考虑到Hostease VPS方案页看看资源档位,回源下限和预热吞吐都取决于底层的 CPU 与内存配额,按峰值规划永远比按均值规划稳妥。

发表评论