4.2 缓存规则与刷新预热:命中率从 40% 到 95%
接入 CDN 的第二天,我兴冲冲打开 CDN 控制台看监控,准备享受命中率 90% 以上的快感。结果命中率曲线稳稳地趴在 40% 上下,回源带宽几乎没怎么降——花了钱,只听到个响。
命中率 40%:先找到谁在拖后腿
CDN 的核心指标就一个:命中率。命中率高,用户请求在节点就地解决,快且省回源流量;命中率低,请求千里迢迢跑回源站,CDN 等于白接。我先拉了一天的访问日志,按 URL 路径分桶统计回源量,很快锁定了三个元凶。
第一个,动态接口被缓存。我的主站域名下同时有页面和 /api/ 开头的接口,接入时图省事用了默认缓存策略,导致部分接口响应被节点缓存,用户刷新不出新数据。这不止是性能问题,是正确性问题——评论接口返回的是别人的评论,谁受得了。
第二个,缓存时间太短。控制台对不同文件类型的默认缓存时间我压根没改过,大部分静态资源只有几分钟。节点上的图片还没焐热就过期,下一次访问又得回源拉一遍。
第三个,URL 参数干扰。我给图片地址带了一个时间戳参数用来强制浏览器更新缩略图,形如 img.example.com/a.jpg?t=20260101。CDN 默认按完整 URL(含参数)做缓存键,参数一变就当成新文件,命中率直接被这批带参请求吃掉一大块。
三句话总结问题和对策:
| 问题现象 | 根本原因 | 解法 |
|---|---|---|
| 接口返回旧数据 | 动态路径被节点缓存 | 按路径设置不缓存 |
| 命中率上不去 | 默认过期时间太短 | 分类型调长缓存时间 |
| 同一图片反复回源 | URL 参数干扰缓存键 | 忽略参数或规范命名 |
分类型缓存规则:一张配置表
理清楚之后,我按文件类型重写了节点缓存过期时间。原则是静态的往死里缓存,动态的一点都不缓存:
| 文件类型 | 路径示例 | 节点缓存时间 | 理由 |
|---|---|---|---|
| 图片 | *.jpg *.png *.webp |
30 天 | 内容生成后不变 |
| JS/CSS | *.js *.css |
7 天 | 发版时用刷新覆盖 |
| HTML 页面 | *.html |
1 小时 | 兼顾更新及时性 |
| 动态接口 | /api/* |
不缓存 | 必须实时 |
| 用户上传中转页 | /upload/* |
不缓存 | 私有内容 |
注意:图片 community 的图片一旦上传就不会改,所以 30 天很安全;如果你的业务允许用户替换同名图片,要么缩短时间,要么养成改完就刷新的习惯。
配置入口在 CDN 控制台的缓存配置里,对应内容分发网络 CDN 文档里的节点缓存过期时间说明。规则是按优先级从高到低匹配,先命中的生效,所以要把不缓存的动态接口规则放在最前面,否则被通配的静态规则先截胡就糟了。
规则匹配还有一个细节:多规则命中时的优先级。控制台里规则列表有先后顺序,排在前面的先生效。我的习惯是按“精确路径 → 文件后缀 → 全部”的顺序排列,动态接口这种精确路径永远置顶。另外建议每条规则加上备注,写清楚为什么这么配,三个月后回来看你会感谢自己。
另外一个隐藏要点:源站的 Cache-Control 响应头会与控制台规则叠加,默认情况下源站返回 no-cache 这类头时,节点可能不按你的配置来。这一点在 4.4 的故障排查里会详细讲,是个能把命中率直接打到零的坑。
带参数请求的处理上,我在控制台开启了忽略参数缓存(图片地址全是无参的固定路径,时间戳只用在需要强刷浏览器的少数场景,换成了走版本号子目录的方案)。改完规则的第二天,命中率从 40% 爬到了 80% 出头,但还没完。
剩下的 20%:爬虫、冷门图与分片
剩下那部分回源我也分析过,主要有两类:一是搜索引擎爬虫和采集器不断请求新增页面和新增图片,这些内容还没被预热过;二是用户上传的新图天然第一次访问必须回源——这部分属于合理成本,没法也不该消灭。想压得更低就得用预热,但预热要消耗回源流量和每日额度,冷门内容预热是亏本买卖。我最后接受的平衡点是:热门内容(首页、排行榜、活动页)手动预热,其余靠自然访问填充。
还有一个技术点:Range 请求。移动端浏览器加载大图会分片请求(Range: bytes=0-1023 之类),CDN 是支持分片缓存的,但如果源站对 Range 请求支持不好(有的应用会忽略 Range 返回完整内容),会导致分片请求反复回源。切到 COS 源之后这个问题彻底消失,因为 COS 原生支持分片访问。
最后给一个调和版的落地建议:第一次接入时把缓存规则批量梳理一遍再保存,而不是一条一条试错。规则修改是全量生效的,改一条等一段时间看数据再改下一条,一个上午就过去了;而按类型一次性配好(图片、JS/CSS、HTML 各一档,动态接口不缓存),再用日志微调,效率完全不同。这套思路第一次配缓存的同学尤其值得照做。
刷新与预热:两个方向相反的操作
规则调好后还剩一个场景:缓存没过期,但内容已经变了。比如我给图片加了水印功能,老用户 30 天内的访问还会命中旧图。这时候就要用到刷新。
刷新分两种,用途完全不同:
| 操作类型 | 作用范围 | 生效粒度 | 我的用法 |
|---|---|---|---|
| URL 刷新 | 指定的一个或多个 URL | 精确 | 改完单张图 |
| 目录刷新 | 指定目录下全部文件 | 粗暴 | 发版整站更新 |
| URL 预热 | 指定 URL 主动推到节点 | 提前 | 活动页上线前 |
刷新是让节点上的缓存失效,用户下次访问时回源拉新;预热是主动把内容拉到节点上,用户第一次访问就直接命中。区别很微妙但很关键:刷新有回源代价(一波用户会触发回源),预热有流量代价(不管有没有人访问都会产生回源流量),两者都不能当饭吃一样乱用。
我用预热的主要场景是活动页:周未搞了个图片主题活动,我把活动页和主视觉图提前预热到各节点,上线瞬间几百个并发全部命中,没有一丝回源压力。站点性能优化的更多套路,可以参考我另一本站点地理优化实战。
提示:刷新有每日额度限制,控制台会显示当日剩余次数。我曾试过整站目录刷新把额度一次性耗光,后面想刷单条记录刷不了,只能干等额度恢复。
刷新和预热还有一个配合套路:发版三步曲。先暂停推送或挂维护页,执行目录刷新清掉旧缓存,再做预热把新内容填进节点,最后解除维护页。这套流程把发版瞬间的回源尖峰完全抹平了,用户侧几乎无感。我现在的发版脚本里这三步是固定动作,手工点点控制台也就两分钟的事。
促使我调优的另一个动力:账单
命中率不只关乎速度,直接就是钱。CDN 计费里回源流量和节点流量是分开的——节点命中的流量按 CDN 价格计,回源部分还要叠加源站的带宽费用。命中率从 40% 到 95%,意味着同样多的用户访问,我的回源成本降到了原来的零头。
另一个省钱点是预热要克制。预热会让内容提前进入各节点,即使无人访问也产生回源流量。预热适合确定会被大量访问的热点内容(活动页、首页轮播图),冷门内容就别预热门槛了,否则账单上会多出一笔纯浪费的流量。
从 40% 到 95%:前后对比
规则、参数、刷新三板斧下去,一周后数据稳定了:
| 指标 | 接入初期 | 优化后 |
|---|---|---|
| 命中率 | 40% | 95% |
| 回源带宽 | 约 4Mbps | 不足 0.5Mbps |
| 成都首屏 | 2.4s → 1.2s | 0.6s |
| 月流量费用 | 无概念,按峰值计 | 可预估 |
命中率的提升带来的不只是速度:节点缓存住的内容,回源请求量同步下降,源站负载随之降低,数据库那边的压力也间接着降了下来。整个链路像被打通了任督二脉,每个环节都更健康。
用户体感最大的变化其实不是首屏数字,而是翻详情页时的连贯感——每张图都是秒开,再没有那种“转圈等一半”的体验。
最后说一句方法论:调缓存规则这件事不要凭感觉,要看日志。控制台的缓存分析能看到哪些 URL 在回源,对着 top 榜一条条改规则,比瞎猜效率高十倍。如果你在排查思路上想找参考,可以看我另一本1Panel 常见问题,里有一套通用的“看日志定位问题”流程,能直接套用到 CDN 调优上。
