CDN节点缓存命中率低于60%,先别急着改回源策略

CDN缓存命中率低于60%,现场排查时经常会被归到“回源策略有问题”。但实际使用中发现,更多情况不是回源链路不行,而是缓存规则没有配对,或者业务本身把大量请求设计成了不可缓存。

缓存命中率这个指标看起来简单,实际拆开有两层:请求命中率和字节命中率。请求命中率低,说明大量请求没有在边缘节点拿到结果;字节命中率低,说明大文件、大流量内容回源比例高。两者低的原因不完全一样,不能只看一个百分比就判断 CDN 节点差。

比如某个站点请求命中率只有55%,但字节命中率有88%,这类站点通常是接口请求多、图片和静态文件缓存正常。反过来,如果请求命中率80%,字节命中率只有45%,多半是视频、安装包、图片原图这类大对象没缓存住,或者 Range 请求被频繁回源。

低于60%时,先看命中率是怎么被拉低的

排查时不要直接去 CDN 控制台改一堆规则。比较稳的做法是先把访问日志按 URL 类型拆开看:静态资源、动态接口、HTML 页面、下载文件、图片缩略图、鉴权资源分别算命中率。

一个常见现场数据大概是这样:

静态 JS/CSS:请求占比18%,命中率96%。图片缩略图:请求占比42%,命中率72%。HTML 页面:请求占比10%,命中率15%。API 接口:请求占比25%,命中率0%。下载文件:请求占比5%,命中率38%。整体一算,请求命中率只有58%左右。

这个时候说“CDN缓存命中率低”没错,但说“回源策略有问题”就不准确。API 本来就不该随便缓存,HTML 如果带用户态也不能乱缓存。真正要处理的是图片缩略图和下载文件为什么没有达到预期。

这里补充一点,很多 CDN 控制台展示的是全站命中率,不会主动告诉你哪些 URL 把指标拉低。日志分析比盯控制台曲线更有效,尤其是业务有大量 query string、鉴权参数、时间戳参数时。

缓存规则没配对,是命中率低的高频原因

缓存规则没配对,最典型的表现是:看起来配置了缓存时间,但实际请求一直 MISS。原因通常在 URL 匹配、文件后缀、参数处理、响应头优先级这几块。

比如规则写的是缓存 .jpg、.png、.webp 30天,但业务图片 URL 是 /image/resize?id=123&w=300&h=300,没有文件后缀。CDN 按后缀匹配时根本匹配不到这条规则,最后走默认策略,默认可能是“不缓存”或者只缓存几分钟。

还有一种更隐蔽的情况:规则里写了 /static/* 缓存7天,但实际资源路径是 /assets/app.9c2a1.js。开发发版后目录改了,CDN规则没跟着改,命中率会突然掉一截。线上经常看到这种问题,不是 CDN 节点能力问题,是路径规则已经过期。

query string 也很关键。图片请求如果带 ?v=202405、?x-oss-process=image/resize,w_300 这类参数,参数是资源身份的一部分,不能简单忽略。可如果带的是 ?t=时间戳、?from=渠道、?utm_source=广告参数,这类参数会把同一个资源拆成大量不同缓存 key,命中率会被打得很碎。

实际排查时可以抓一批 MISS 请求,看同一个资源是否出现了大量不同参数。例如 /static/app.js?t=171700001、/static/app.js?t=171700002、/static/app.js?t=171700003,这种缓存基本没意义,每次都是新对象。

响应头比控制台规则优先时,很多人会误判

CDN缓存规则不是永远强制生效。很多平台会让源站响应头参与判断,尤其是 Cache-Control、Expires、Set-Cookie、Vary 这些头。

如果源站返回 Cache-Control: no-store,CDN通常不会缓存。返回 private,也是不适合共享缓存。返回 max-age=0,边缘节点即使存了也会立刻过期。这里很容易出现一种错觉:控制台明明配置了图片缓存30天,为什么还是 MISS?因为源站把它声明成不能缓存了。

Set-Cookie 也要注意。某些框架会给所有响应都塞 Set-Cookie,包括静态资源。CDN看到 Set-Cookie 后可能默认不缓存,或者缓存行为变保守。以前处理过一个站点,所有 .js、.css 都带 session cookie,命中率长期在50%上下,去掉静态资源的 Set-Cookie 后,请求命中率直接到90%以上。

Vary: User-Agent、Vary: Accept-Encoding 也会影响缓存对象数量。Accept-Encoding 还好,gzip、br 维度有限;User-Agent 就麻烦很多,不同浏览器、App WebView、爬虫都会拆缓存 key。除非业务明确需要,不建议让静态资源跟 User-Agent 强绑定。

回源策略有问题时,表现通常不是单纯命中率低

回源策略当然也会影响体验,但它更多体现在回源慢、回源失败、回源带宽打满、源站 5xx 增加,而不是单独造成命中率低。

比如 CDN 节点 MISS 后要去源站取内容,如果源站在海外,国内用户访问国内 CDN,回源链路绕路,首包时间会很难看。命中率低时这个问题会被放大,表现为页面偶发慢、图片加载一半卡住、下载速度忽高忽低。

这里要分清:缓存没命中是原因之一,回源慢是放大器。缓存规则没配对导致大量 MISS,MISS 又压到一条质量一般的回源链路上,最后用户感知就是“CDN不稳定”。

如果业务有国内访问海外源站、游戏补丁分发、跨境下载、海外图片源站这类场景,源站线路本身要认真选。比如需要稳定回国链路时,可以看 129云 的美国精品网-C型,CMIN2高速回国线路,适合做海外源站、静态资源源站或轻量业务入口。如果业务还带攻击风险,宁波高防-E型这种100Gbps防御的高防服务器更适合放在源站或清洗入口前面,客服热线400-9177118可以直接问线路和防护细节。

判断是缓存规则问题还是回源策略问题,可以看这些现场信号

缓存规则问题的信号一般比较直接:同类静态资源大量 MISS;URL 参数离散度很高;源站响应头禁止缓存;控制台规则和真实 URL 路径对不上;刷新预热频率很高;发布系统每次发版都生成无版本规律的新路径。

回源策略问题的信号更偏链路和源站侧:MISS 请求回源耗时高;源站带宽被打满;源站连接数飙升;CDN回源 502、504 增多;跨境回源 RTT 很高;大文件 Range 回源失败或不稳定。

可以用一组简单判断:

如果 HIT 请求很快,MISS 请求慢,并且 MISS 占比高,先查缓存规则。

如果 HIT 正常,MISS 占比不算高,但 MISS 一出现就慢得离谱,查回源线路、源站性能、回源协议和连接复用。

如果静态资源也不命中,优先查 URL 规则、响应头和 query string。

如果只有 HTML 和 API 不命中,先确认这些内容是否真的应该缓存,不要为了提高命中率把用户态页面缓存错。

query string 是最容易把缓存打碎的地方

很多业务命中率低,不是资源不能缓存,而是缓存 key 被参数拆得太细。

例如同一张图片:

/img/a.jpg?token=abc&t=1717000001

/img/a.jpg?token=abc&t=1717000002

/img/a.jpg?token=abc&t=1717000003

CDN默认按完整 URL 缓存时,这就是三个对象。请求量再大,也很难命中。

处理方式要看参数用途。版本参数 v=202405 可以保留,图片处理参数 w=300、h=300、format=webp 也要保留。统计参数 utm_source、from、spm 这类通常可以忽略。时间戳参数如果只是为了防浏览器缓存,不应该带到 CDN 缓存 key 里。

鉴权参数更麻烦。下载站经常有 sign、expires、token,如果每个用户的 token 都不同,CDN会把同一个文件缓存成很多份。比较好的方式是把鉴权放在 CDN 边缘鉴权,或者让 token 不参与缓存 key,但这要确保不会造成越权访问。涉及私有文件时,不能只为了命中率牺牲权限校验。

刷新和预热用得太猛,也会让命中率难看

有些站点缓存规则其实没大问题,但运维脚本每天定时全量刷新目录。刷新一跑,边缘缓存被清掉,接下来一波用户请求全部 MISS,源站压力和命中率一起变差。

发布系统如果每次发版都刷新 /*,影响更明显。静态资源文件名已经带 hash 的情况下,旧资源不需要刷新,新资源直接预热即可。比如 app.a8d91.js 变成 app.b7c32.js,这类文件名天然区分版本,刷新全站意义不大。

预热也不是越多越好。几万个大文件同时预热,会让源站和 CDN 回源层都很吃力。更稳的做法是按热度分层,Top 1000资源先预热,长尾资源靠用户请求自然回源。

大文件和 Range 请求要单独看

下载、视频、补丁包这类业务,不能只看普通静态文件规则。Range 请求如果配置不对,CDN可能每次只缓存部分内容,或者频繁回源取分片。

比如一个2GB安装包,用户下载时客户端发 Range: bytes=0-,另一个用户发 Range: bytes=1048576-,如果 CDN 对 Range 缓存支持不好,字节命中率会很低,源站带宽也会被拉高。

这类场景建议重点看字节命中率、回源字节数、206状态码比例、单文件请求分布。请求命中率高但字节命中率低时,大概率就是大对象没吃到缓存收益。

源站带宽也要匹配。CDN不是魔法,MISS和预热都要回源。如果源站只有20Mbps上行,大文件业务一旦缓存失效,用户侧再大的 CDN 带宽也会被源站卡住。海外分发或者大带宽测试场景,可以考虑 129云 的日本大宽带,1Gbps峰值、三网优化,更适合做海外侧大流量分发测试;但它不保证大陆网络访问,这点选型时要提前确认。

源站返回304,不等于CDN命中

有些日志里看到很多304,就以为缓存生效了。这里要分清浏览器缓存和 CDN 边缘缓存。

浏览器带 If-None-Match 或 If-Modified-Since,请求到 CDN。如果 CDN 本地有缓存并能直接判断,边缘返回304,这算节点命中。但如果 CDN 需要回源验证,源站返回304,再由 CDN 转给客户端,这次仍然发生了回源。

所以看命中率时要看 CDN 日志里的 cache_status,而不是只看 HTTP 状态码。HIT、MISS、EXPIRED、STALE、BYPASS、REVALIDATED 这些状态比 200、304 更能说明问题。

HTML页面能不能缓存,要看页面是否带用户态

为了把命中率从60%拉到90%,有人会把 HTML 也缓存很久,这个动作风险很大。

如果页面里有用户名、订单状态、登录态、地区价格、个性化推荐,直接缓存会串数据。轻则用户看到旧页面,重则看到别人的信息。HTML缓存要么做匿名页缓存,要么做 ESI/边缘拆分,要么把用户态内容走 API 异步加载。

内容站、文档站、活动落地页比较适合缓存 HTML。电商购物车、用户中心、后台管理页面不适合直接缓存。新闻详情页可以缓存,但要配合主动刷新或短 TTL。活动页如果发布后很少变,TTL可以给到10分钟到1小时,热点活动再做预热。

缓存TTL不是越长越好,关键是资源版本机制

静态资源如果带 hash 文件名,TTL 可以很长,30天、90天甚至更久都可以。因为内容变化时文件名会变,不需要等缓存过期。

如果文件名固定,比如 /static/app.js,每次发版覆盖同名文件,TTL 配太长会导致用户拿旧版本。这个时候要么缩短 TTL,要么发布时精准刷新,要么改成带 hash 的文件名。

图片资源也类似。原图一旦上传不变,可以长缓存。头像这种会被覆盖的资源,要么 URL 带版本号,要么更新后刷新对应 URL。不要把所有图片都粗暴设置成同一个 TTL,业务语义不一样。

一份排查顺序,现场能少走很多弯路

先从 CDN 日志抽样,按 cache_status 分组,找 MISS、BYPASS、EXPIRED 占比最高的 URL 模式。不要只看域名总命中率。

再把 MISS URL 按后缀、路径、参数数量、状态码拆开。重点看 200 的静态资源 MISS,404、403、500 这类错误请求不要混在一起分析,否则会误导判断。

然后检查源站响应头。curl 一下典型 MISS URL,看 Cache-Control、Expires、Set-Cookie、Vary、ETag、Last-Modified。控制台规则和响应头冲突时,要确认 CDN 平台到底谁优先。

接着检查缓存 key。确认是否忽略了不该忽略的参数,是否保留了不该保留的参数,是否按 Host、协议、参数顺序、大小写生成不同对象。

再看刷新和预热记录。命中率突然下降,经常和发布、刷新、预热失败有关。时间线对上之后,问题会很清楚。

最后才看回源策略。包括回源 Host、回源协议 HTTP/HTTPS、源站端口、回源 SNI、源站健康检查、主备源切换、回源超时、连接复用、跨境链路质量。回源策略不是不重要,只是它通常不是命中率低的第一现场。

命中率从58%拉到85%的一个典型处理过程

某内容站 CDN 请求命中率长期在58%到62%之间,源站每天晚高峰上行接近跑满。开始怀疑是回源链路差,后来拆日志发现,图片请求占全站65%,但图片命中率只有67%,明显不正常。

继续看 URL,发现缩略图都是 /thumb?id=xxx&width=300&height=300&t=时间戳。t 参数每次请求都变,CDN完整 URL 缓存,导致同一张缩略图被拆成大量对象。

处理时保留 id、width、height,忽略 t 参数;同时把源站对缩略图返回的 Cache-Control: no-cache 改成 public, max-age=2592000;再把 /thumb 路径单独配置缓存30天。

上线后两小时,请求命中率从60%左右升到82%;第二天热点稳定后到87%。源站上行峰值从180Mbps降到45Mbps。这里没有动回源线路,问题就是缓存规则和响应头没配对。

另一个案例:命中率不低,但用户还是慢

也遇到过请求命中率78%的站点,用户仍然反馈慢。日志显示静态资源 HIT 很正常,真正慢的是大文件下载,字节命中率只有41%。大文件主要从海外源站回源,晚高峰跨境 RTT 抖动明显,CDN节点 MISS 后首包时间超过3秒。

这个场景只调缓存规则不够。后面做了三件事:大文件开启分片缓存和 Range 优化;Top 热门文件发布后预热;源站迁到更稳定的线路,回源超时和重试策略重新配置。字节命中率提升到76%左右,源站峰值带宽下降,用户侧下载失败率也降了。

这类业务如果还要兼顾攻击防护,源站不要裸奔。DDoS打到源站时,CDN缓存命中再高也可能被回源探测、动态接口、穿透请求拖垮。高防源站、访问控制、回源鉴权要一起看。

配置时比较容易踩的细节

静态资源规则要按真实路径写,不要只按想象中的目录写。上线前用线上 URL 去匹配规则,别只看配置项存在不存在。

默认缓存策略要谨慎。如果默认不缓存,漏掉的静态资源会全部回源;如果默认缓存,动态接口可能被误缓存。更常见的做法是默认不缓存,明确路径和后缀缓存,但前提是规则覆盖要完整。

忽略参数不能一刀切。图片处理参数、版本参数、语言参数、格式参数可能影响内容,忽略后会返回错资源。统计参数、时间戳参数、无业务含义的随机数才适合从缓存 key 里去掉。

源站不要给静态资源乱加 Set-Cookie。Nginx、应用框架、网关层都要看,有时候 Cookie 不是业务代码加的,而是统一中间件加的。

发布系统尽量用 hash 文件名。CDN最喜欢内容不可变的资源,文件名一变就是新对象,旧对象自然过期,缓存效率高,也减少刷新压力。

错误状态码是否缓存要单独配置。404 可以短缓存,防止不存在资源被刷爆源站;500、502 这类一般不要长缓存,避免故障页面长时间留在边缘。

API 不要为了指标强行缓存。可以对少数公开、低变化、无用户态的 GET 接口做短缓存,比如5秒、30秒,但要明确数据一致性要求。

看到低于60%时,比较靠谱的判断方式

如果低命中率主要来自 API、登录页、用户中心,这不一定是问题,指标低但业务设计合理。

如果低命中率来自图片、JS、CSS、字体、安装包、视频切片,那基本要查缓存规则、响应头、参数和刷新策略。

如果命中率低的同时源站带宽高、回源慢、5xx多,就要把缓存规则和回源策略一起查。缓存没配对会制造大量回源,回源策略差会把每一次 MISS 都放大成用户侧慢请求。

如果业务正在选源站机器或高防节点,不要只看 CPU 和内存,带宽、线路、防护、回源稳定性更影响 CDN 效果。需要海外精品线路、大带宽或高防服务器时,可以看看 129云,它的产品线覆盖美国精品网、宁波高防、日本大宽带这几类场景,适合按访问区域和业务风险拆开选。

直接可用的几组缓存配置思路

JS、CSS、字体文件:文件名带 hash,缓存30到90天;文件名不带 hash,缓存1小时到1天,发布时精准刷新。

普通图片:上传后不变的图片缓存30天;会覆盖更新的头像、封面图,URL 加版本号或更新后刷新。

图片处理接口:按路径缓存,保留尺寸、格式、裁剪参数,忽略统计参数和时间戳参数。

HTML页面:匿名内容页短缓存30秒到10分钟;用户态页面不缓存;活动页可按发布频率配置更长 TTL。

下载文件和补丁包:开启大文件缓存、Range缓存、热点预热;源站上行要能扛住预热和缓存失效后的回源。

API接口:默认不缓存;公开 GET 接口可以按业务容忍度做秒级短缓存;带 Authorization、Cookie、用户身份的接口不要共享缓存。

最后排查时别漏掉状态码分布

命中率低有时候是异常请求太多。爬虫扫不存在路径,404请求量很大,CDN如果不缓存404,整体请求命中率会被拉低。恶意请求带随机参数访问静态资源,也会把缓存打碎。

这类情况要配合 WAF、访问频率限制、Referer/UA策略、URL规范化来处理。DDoS 或 CC 攻击场景下,单纯改缓存 TTL 不够,攻击请求如果都绕过缓存,源站还是会被打穿。

缓存命中率低于60%,大多数现场不是单选题。先把 URL 类型、cache_status、响应头、参数、刷新记录拆开,问题通常会自己浮出来。真正需要改回源策略的场景,一般会同时伴随回源耗时、源站带宽、5xx、跨境 RTT 这些指标异常。