CDN节点缓存命中率低,排查时别一上来就怀疑节点

CDN缓存命中率低,现场最容易出现的误判是:看到回源带宽上涨、源站CPU升高,就直接判断CDN节点缓存有问题。实际使用中发现,很多命中率低并不是节点不缓存,而是请求本身被拆得太碎、缓存规则没覆盖、源站响应头把缓存拦住了,或者业务在URL里塞了太多动态参数。

排查这类问题,建议先把“命中率低”拆开看:是整体命中率低,还是某个目录、某类文件、某个区域、某个时间段低。只看控制台上的总命中率,很容易被动态接口、鉴权请求、回源刷新任务一起拉低。

比如一个站点日请求量1000万,其中图片、JS、CSS这些静态资源800万次,接口请求200万次。如果静态资源命中率95%,接口基本不缓存,那么全站请求命中率大概是76%。看起来不算高,但静态部分其实没大问题。反过来,如果静态资源只有60%,那才是需要重点处理的对象。

先看统计口径:请求命中率和流量命中率不是一回事

排查前要确认看的是哪种命中率。请求命中率按请求次数算,流量命中率按字节数算。两者差很多时,说明资源大小分布有问题。

常见情况是:小文件命中不错,大文件命中差,请求命中率看着还行,但流量命中率很低。视频、安装包、模型文件、游戏补丁包这类资源,只要有一批大文件频繁回源,源站带宽会很明显。

可以按下面这种方式看数据,不一定非得表格工具,日志里按扩展名、目录、状态码聚合就能看出问题:

资源类型 | 请求占比 | 流量占比 | 请求命中率 | 流量命中率 | 判断

图片jpg/png/webp | 45% | 25% | 96% | 94% | 基本正常

JS/CSS | 18% | 8% | 92% | 90% | 可继续优化版本号

视频mp4 | 8% | 50% | 55% | 48% | 重点排查Range、缓存时间、热度分散

API接口 | 25% | 10% | 5% | 4% | 不要拿它拉低全站结论

其他 | 4% | 7% | 40% | 35% | 看是否有异常路径

URL是否被参数打散,是命中率低的高频原因

CDN缓存的基本索引通常是Host + URI + Query String,再结合一些配置项。也就是说,同一个文件,如果URL参数不同,CDN可能会当成不同对象缓存。

实际使用中发现,很多业务喜欢在静态资源后面拼参数,比如:

/static/app.js?v=20240501

/static/app.js?t=1717123001

/static/app.js?from=home

/static/app.js?utm_source=ad

如果v是版本号,这还算合理;如果t是时间戳,每次请求都不同,那等于告诉CDN“每次都是新资源”。这种场景下,节点不是不缓存,而是缓存对象被无限拆分,永远凑不出热度。

排查Query String时重点看这些字段

时间戳类参数:t、time、timestamp、_、random、rand。这类参数如果出现在静态资源上,通常会严重影响缓存。

统计类参数:utm_source、utm_campaign、from、channel。广告投放、活动页常见,如果这些参数不影响资源内容,建议在CDN侧配置忽略。

鉴权类参数:token、sign、expires。这类参数不能简单忽略,要看鉴权是在CDN边缘完成,还是必须回源校验。处理不好会有安全问题。

多说一句,忽略参数不是越多越好。图片处理参数如w、h、format、quality会影响实际内容,不能随便忽略。/image/a.jpg?w=200和/image/a.jpg?w=800不是一个文件。

缓存规则没覆盖到真实路径,控制台看着配了,实际没生效

CDN缓存规则一般按后缀、目录、全路径、正则来匹配。现场经常遇到一种情况:控制台配置了jpg、png缓存30天,但实际业务图片路径是/image/resize?id=123,这种没有后缀的动态路径不会命中后缀规则。

还有一种是规则优先级问题。比如配置了:

/*.jpg 缓存30天

/api/* 不缓存

/* 缓存10分钟

如果业务路径是/api/image/1.jpg,实际走哪条规则要看CDN厂商的优先级设计。有的按最长路径匹配,有的按配置顺序,有的正则优先。排查时不要只看“有没有规则”,要看某个具体URL最终命中了哪条规则。

建议拿真实URL逐条验证

不要用脑补路径。直接从访问日志里抓Top URL,拿请求量最高、回源最多的URL去验证。比如:

curl -I 'https://www.example.com/static/app.8f3a.js'

curl -I 'https://www.example.com/image/resize?id=1001&w=400'

curl -I 'https://www.example.com/download/client.pkg?version=202405'

看响应里的Age、X-Cache、Via、Cache-Control、Expires等头。有些CDN会返回HIT、MISS、BYPASS、EXPIRED、REVALIDATED,不同状态含义不同。BYPASS通常表示规则或响应头要求不缓存,EXPIRED表示缓存过期后回源验证,MISS表示节点没有对象。

源站响应头经常把缓存拦住

CDN不是完全无视源站的。源站返回的Cache-Control、Set-Cookie、Vary这些头,都会影响缓存行为。特别是Web框架默认返回的一些头,经常让静态资源也无法正常缓存。

需要重点检查这些响应头:

Cache-Control: no-store —— 明确禁止缓存,CDN一般不会存。

Cache-Control: no-cache —— 不是完全不缓存,但每次使用前需要回源验证,命中率和回源表现都会受影响。

Cache-Control: private —— 表示给单个用户缓存,不适合共享缓存,CDN通常不缓存。

Set-Cookie —— 很多CDN默认不缓存带Set-Cookie的响应,尤其是HTML和动态路径。

Vary: * —— 基本会破坏共享缓存。

Vary: Accept-Encoding —— 正常,gzip/br区分缓存对象可以接受。

Vary: User-Agent —— 小心,会把缓存按UA拆碎,移动端、浏览器、爬虫各自形成对象,命中率下降。

这里补充一点,源站Nginx里经常有这种配置:

add_header Cache-Control no-cache;

本意可能是给HTML用,结果配置在server级别,所有静态资源都带上了。CDN侧即使配置了缓存30天,也可能被源站头覆盖,或者表现成需要回源验证。排查时要看最终返回给CDN的源站响应,不要只看浏览器拿到的结果,因为CDN可能二次改写过响应头。

Range请求会让大文件缓存表现变复杂

视频、音频、下载包、游戏补丁经常使用Range请求。浏览器或客户端下载大文件时,不一定从头到尾顺序拉,而是请求:

Range: bytes=0-

Range: bytes=1024000-2047999

Range: bytes=5000000-

如果CDN没有开启Range缓存,或者源站对Range响应不稳定,节点可能频繁回源。表现就是小文件命中正常,大文件流量命中率低,源站出口压力一直下不来。

排查时看状态码206的比例。正常大文件场景206很多,但如果206几乎都MISS,就要看CDN是否支持分片缓存、是否开启Range回源、源站是否返回Accept-Ranges: bytes。

大文件场景还要看预热和热度

大文件不是放到CDN上就一定命中高。比如一个2GB安装包,用户分布在全国多个区域,每个区域访问量都不高,边缘节点刚缓存一部分就过期或被淘汰,命中率自然上不去。

这种场景可以考虑做文件预热,把发布后的版本包主动推到核心节点。游戏更新、客户端安装包、短时间集中下载的活动资源,都适合预热。相反,如果文件数量很多、单文件访问很少,预热效果也一般,容易浪费节点缓存空间。

缓存时间太短,或者被频繁刷新

TTL设置太短是很直接的原因。比如图片设置缓存5分钟,每个节点过期后都要回源验证。访问量不大的资源,在一个节点上可能还没形成热度就过期了。

静态资源建议采用“文件名带版本号 + 长缓存”的方式,比如:

/static/app.20240531.js

/static/app.8f3a91.css

/assets/logo.v2.png

这类资源内容变化时URL也变化,可以放心缓存7天、30天甚至更久。不要用固定文件名app.js然后频繁刷新CDN。刷新操作是把缓存删掉,刚刷新完的一段时间,所有节点都要重新回源,命中率会明显下滑。

遇到发布后命中率突然下降,要查发布系统有没有自动全站刷新。有些CI/CD脚本为了省事,每次上线都调用“刷新目录 /”或“刷新全部URL”,这会把原本稳定的缓存打空。发布频率高的站点,这个问题很常见。

动态内容混在静态域名下,会污染整体命中率

CDN域名最好按内容类型拆开。静态资源、下载资源、API接口、图片处理、视频点播,不要都塞在一个域名下然后看一个总命中率。

比如www.example.com同时承载首页HTML、API、图片、JS、下载包。HTML可能要求短缓存或不缓存,API大多不缓存,图片希望长缓存,下载包希望Range缓存。所有东西混在一起,规则容易互相影响,统计也不好判断。

更清晰的做法是:

static.example.com:JS、CSS、字体、图片,长缓存。

download.example.com:安装包、补丁包,关注Range和预热。

api.example.com:接口,加速链路为主,不以缓存命中率作为核心指标。

media.example.com:视频、音频,单独看206、分片、拖拽播放。

如果业务出海或者面向海外用户,源站和CDN回源链路也要一起看。比如源站在中国香港,覆盖东南亚、华南用户,对延迟比较敏感,可以选带CN2或精品线路的服务器做源站;如果下载资源主要面向欧美,源站带宽更重要。购买或迁移源站时,可以看看129云的香港精品CN2-B型、美国精品大宽带-A型、德国大宽带等产品,像游戏更新包、企业下载站、高防业务这类场景会更关注带宽质量、线路和防护能力,客服热线400-9177118可以直接问线路覆盖和适配场景。

节点缓存被淘汰,不一定是TTL到期

CDN节点缓存空间不是无限的。即使你配置了缓存30天,冷资源也可能因为节点空间压力被提前淘汰。淘汰后再次访问就是MISS。

这种问题在“资源数量极多、单资源访问稀疏”的业务里很明显,比如UGC图片站、海量头像、短视频封面、文件分享系统。总访问量很大,但每个文件平均访问次数低,缓存很难沉淀。

排查时可以看Top URL集中度。如果Top 1万 URL只占总请求的10%,说明资源非常分散;如果Top 1万 URL占到70%,命中率通常更容易做高。

这里有个粗略判断:

Top 1千 URL占比超过50%:热点集中,命中率应该能做得比较高。

Top 1万 URL占比在30%到60%:中等分散,规则和TTL优化后会有改善。

Top 10万 URL占比还不到30%:长尾很重,别只盯着命中率,要评估回源成本和源站扩展能力。

地域和运营商维度也要拆开看

有时候全站命中率低,是某个区域低。比如华东、华南命中率90%以上,西北只有50%,海外更低。这可能和节点覆盖、调度、访问热度有关。

CDN边缘节点是按区域和运营商分布的。某个资源在广东电信节点很热,不代表在北京联通节点也热。用户访问分散时,每个节点都要各自建立缓存,冷启动阶段会产生不少回源。

排查时可以按这些维度切:

区域:华北、华东、华南、西南、港澳台、海外。

运营商:中国电信、中国联通、中国移动、教育网、海外ISP。

协议:HTTP/1.1、HTTP/2、HTTPS。

节点层级:边缘节点、上层节点、父节点回源。

多说一句,BGP、CN2、GIA这些线路更多影响访问质量和回源质量,不会直接让缓存命中率变高。但当CDN需要回源时,线路质量会影响回源延迟和源站压力的体感。如果命中率短时间没法做高,源站线路和带宽就更关键。

HTTPS、Header和Cookie把缓存对象拆碎

除了URL,Header也会影响缓存。尤其是Cookie和Vary。

有些站点所有请求都带Cookie,包括静态资源:

Cookie: uid=xxx; session=xxx; abtest=A

如果CDN配置成按Cookie区分缓存,或者源站返回Vary: Cookie,那么同一个JS文件会因为不同用户Cookie不同而形成不同缓存对象,命中率会很差。

静态资源域名尽量不要下发业务Cookie。Cookie的Domain不要写成.example.com覆盖所有子域,避免static.example.com也带上登录态Cookie。这个问题在浏览器侧很隐蔽,但从CDN访问日志里看请求头会非常明显。

压缩策略不一致,也会影响缓存观察

gzip、br、zstd压缩本身不是坏事,但要注意缓存对象可能按Accept-Encoding区分。比如同一个/app.js,对支持br的浏览器缓存一份br版本,对只支持gzip的客户端缓存一份gzip版本。

正常情况下这没问题,最多对象数量增加一点。但如果源站压缩策略不稳定,比如有时返回gzip,有时返回未压缩,或者边缘和源站都在压缩,响应头Content-Encoding混乱,就会出现命中状态不好判断、部分客户端异常、缓存反复重建等问题。

建议静态文件压缩策略固定下来。JS、CSS、JSON、SVG适合压缩;图片、视频、zip、gz、br这类已经压缩过的文件不要重复压缩。

状态码维度不能漏,特别是301、302、404

命中率统计里经常混入大量非200请求。比如图片不存在返回404,短链跳转返回302,HTTP跳HTTPS返回301。这些响应是否缓存,要看CDN规则和源站头。

如果404不缓存,爬虫或异常客户端持续请求不存在的资源,会造成大量回源。UGC业务里这种情况很常见,用户删除了图片,但外部页面还在引用。

可以按状态码聚合:

200:重点看静态资源和大文件。

206:重点看Range缓存。

301/302:看跳转是否必要,是否可缓存。

304:说明有回源验证或客户端验证,要结合Age看。

403:可能是防盗链、鉴权、WAF规则导致。

404:异常请求、历史资源、爬虫扫描都可能造成回源。

5xx:源站异常,不要和缓存问题混在一起判断。

对于稳定的404资源,可以考虑短时间缓存404,比如60秒到5分钟,防止异常请求打穿源站。但用户刚上传、刚生成的资源路径不要随便缓存404,否则会出现“明明文件已经有了,用户还看到不存在”的问题。

防盗链、鉴权和缓存的关系要理清

很多业务为了防盗链,会在URL里加sign和expires:

/video/a.mp4?sign=abc&expires=1717200000

如果每个用户的sign都不同,CDN默认按完整URL缓存,命中率一定会下降。视频、下载、防盗链业务经常遇到这个问题。

处理方式通常有两类。边缘鉴权:CDN节点校验sign,通过后按去除签名参数的URL缓存资源。回源鉴权:每次都要源站判断,缓存收益会小很多。

边缘鉴权更适合大流量静态内容,但要确保签名规则、过期时间、防盗链策略都在CDN侧正确配置。不要简单忽略sign参数,否则可能绕过权限控制。

回源Host和源站响应是否一致

有些命中率问题看起来在CDN,实际是回源Host配置不一致导致源站返回不同内容。比如用户访问cdn.example.com,CDN回源到origin.example.com,但回源Host写成了cdn.example.com或默认IP,源站Nginx匹配到了错误的server块,返回了不同Cache-Control。

这种问题排查时要模拟CDN回源:

curl -I -H 'Host: www.example.com' http://1.2.3.4/static/app.js

curl -I -H 'Host: origin.example.com' http://1.2.3.4/static/app.js

对比Cache-Control、Expires、Set-Cookie、Content-Type、Content-Length。只要源站在不同Host下返回头不一致,CDN缓存行为就可能和预期不一样。

刷新、预热、发布系统要一起查日志

CDN命中率突然从90%掉到40%,这种突变通常不是自然流量造成的。重点看三个动作:刷新、预热、发布。

刷新会删缓存,预热会主动拉资源,发布会改变URL或响应头。三者任何一个操作异常,都能影响命中率。

常见现场问题包括:

上线脚本每次刷新整个目录。

运营后台批量刷新图片目录。

预热列表生成错误,把低频资源预热了一大批,高频资源反而没预热。

发布后文件名没变,但内容变了,只能靠刷新解决,导致节点反复回源。

灰度发布期间,不同源站返回不同版本,CDN缓存对象混乱。

如果有多源站或对象存储回源,还要看源站之间文件是否一致。A源站返回Cache-Control: max-age=2592000,B源站返回no-cache,CDN负载到不同源时表现就会飘。

日志里真正有价值的字段

排查命中率,控制台图表只能定位趋势,细节还是要靠日志。建议至少拿到这些字段:

time:请求时间,用来看发布、刷新、异常流量时间点。

client_ip和region:判断区域和运营商分布。

host、uri、query:判断缓存对象是否被拆散。

status:区分200、206、301、302、404、5xx。

cache_status:HIT、MISS、BYPASS、EXPIRED等。

bytes_sent:计算流量命中影响。

referer:发现盗链、异常引用、爬虫入口。

user_agent:发现爬虫、下载器、异常客户端。

range:判断大文件分片请求。

upstream_status和upstream_time:判断回源质量。

content_type:区分图片、视频、JS、HTML、接口。

拿到日志后不要急着全量看。先按host聚合,再按目录聚合,再按扩展名聚合,再看Top MISS URL。Top MISS URL往往比全站平均值更有意义。

一个现场排查路径可以这样走

看命中率下降发生在哪个时间段

如果是持续低,偏配置或业务形态问题。如果是突然低,偏发布、刷新、源站头变化、异常流量。把命中率曲线和发布记录、刷新记录、带宽曲线叠在一起看,很多问题会直接暴露。

把静态和动态拆开算

不要拿API接口要求CDN命中。先过滤出jpg、png、webp、js、css、woff、mp4、zip这类资源,看它们自己的命中率。静态资源低,才继续深入缓存规则、URL参数、响应头。

抓Top MISS URL

Top MISS URL能告诉你是单个热点资源没缓存,还是大量长尾资源自然MISS。热点资源MISS通常是配置问题,长尾资源MISS则可能是业务资源分布问题。

用curl验证响应头和缓存状态

同一个URL连续请求两到三次。如果第一次MISS、第二次HIT,多半缓存基本正常;如果每次都是MISS或BYPASS,就看规则、响应头、Cookie、Query String。

检查源站原始响应

绕过CDN访问源站,确认Cache-Control、Set-Cookie、Vary、Accept-Ranges。源站头不干净,CDN侧很难稳定缓存。

再看区域、运营商、大文件Range

如果普通静态资源正常,大文件流量命中率低,重点转向Range和预热。如果国内正常、海外低,重点看海外节点热度、回源链路和源站位置。

容易被忽略的异常流量

爬虫、扫描器、下载器会明显拉低命中率。尤其是扫描器请求大量不存在路径,全部404回源;下载器带随机参数绕缓存;爬虫不接受压缩或频繁请求历史URL。

日志里如果看到某些UA请求量异常、Referer为空、路径随机、状态码404占比高,可以考虑限速、WAF规则、缓存404、Referer策略。高防场景下还要区分正常MISS和DDoS或CC攻击造成的回源放大。CDN缓存不是抗所有攻击的万能层,动态请求和随机URL攻击照样可能打到源站。

如果源站本身还承载游戏服、企业业务或高防入口,建议源站带宽和防护能力留余量。像129云提供的高防服务器租用、G口大带宽服务器、海外云计算方案,就比较适合把源站能力和CDN加速一起规划,避免命中率波动时源站直接被打满。

一些配置取值的经验范围

HTML:通常0到5分钟,活动页可更短,纯静态落地页可按版本长缓存。

JS/CSS:文件名带hash时7到30天,甚至更长;不带hash时谨慎设置,避免发布后旧版本残留。

图片:7到30天比较常见,头像、验证码、临时图要单独处理。

字体文件woff/woff2:30天以上通常没问题。

视频mp4:看更新频率,常见7到30天,配合Range缓存和预热。

安装包、游戏补丁:版本化文件可以30天以上,发布前预热核心区域。

API:大多不缓存,少量公开配置接口可以缓存5到60秒。

404:异常请求多时可缓存60到300秒,但生成型资源要谨慎。

这些值不是直接照抄就能解决问题,关键还是看URL是否稳定、内容是否会变、是否带用户态、是否允许共享缓存。

命中率低时,最该避免的操作

不要一看到旧内容就全站刷新。全站刷新会把已有缓存清空,短时间回源压力很大。应该优先改成版本化文件名,或只刷新明确变更的URL。

不要在静态资源上加随机参数。防缓存可以在开发环境用,线上静态资源这么做会直接破坏CDN缓存。

不要让静态域名带业务Cookie。Cookie会增加请求头体积,也可能影响缓存键。

不要把所有业务都放一个CDN域名下看总命中率。接口、HTML、图片、下载混在一起,优化方向会被数据带偏。

不要忽略源站响应头。CDN控制台配了缓存时间,不代表最终一定按这个时间缓存,源站头和CDN优先级要一起看。

最后看一个典型故障片段

某下载站源站带宽从300Mbps涨到1.8Gbps,CDN控制台显示流量命中率只有42%。一开始怀疑节点缓存异常,后来按日志拆开看,发现Top流量集中在几个2GB左右的安装包上,状态码206占比超过80%,且206几乎都是MISS。

继续查响应头,源站没有返回Accept-Ranges: bytes,CDN侧也没有开启Range缓存。客户端下载器并发分片下载,每个分片都回源,CDN只缓存了少量完整对象,实际挡不住流量。

处理动作是:源站开启Range支持,CDN开启分片缓存和Range回源,发布前对Top安装包做预热,安装包URL改成版本化路径。调整后请求命中率从68%升到83%,流量命中率从42%升到91%左右,源站出口回落到200Mbps以内。

这个案例里,问题不在“CDN有没有缓存”,而在大文件访问方式和缓存策略不匹配。类似问题在视频、游戏补丁、ROM包、离线地图包里都很常见。