CDN节点缓存命中率低了源站服务器压力会翻几倍
CDN节点缓存命中率一掉,源站压力不是线性增加
CDN看起来是把静态资源放到边缘节点上,用户就近访问,源站少扛流量。实际使用中发现,很多故障并不是带宽突然暴涨,而是缓存命中率从一个看似还可以的数值掉下来,源站连接数、CPU、磁盘IO、数据库查询一起被拖上去。
比如一个站平时CDN命中率在95%,源站只需要处理5%的回源请求。某次改版后Cache-Control没配好,命中率掉到80%。表面看只是少了15个百分点,源站实际回源比例从5%变成20%,源站请求量直接变成原来的4倍。
这就是很多人容易低估的地方:CDN命中率不是简单看百分比高不高,而是要看未命中的那一小段有多大。对源站来说,命中的95%和命中的80%,差异不是15%,而是回源压力差了4倍。
用一组数据看得更直观
假设业务入口每秒有10000个请求,资源主要是图片、JS、CSS、接口缓存数据,CDN正常承接访问。
| CDN缓存命中率 | 回源比例 | 源站QPS | 相对95%命中率的源站压力 |
|---|---|---|---|
| 99% | 1% | 100 | 0.2倍 |
| 95% | 5% | 500 | 1倍 |
| 90% | 10% | 1000 | 2倍 |
| 80% | 20% | 2000 | 4倍 |
| 70% | 30% | 3000 | 6倍 |
线上排查时,95%到90%经常被误判为“小波动”。但源站看到的是QPS翻倍。再叠加TLS握手、短连接、动态接口、数据库查询,后面的链路不一定扛得住。
多说一句,CDN控制台里看到的“命中率”还要分清楚是请求命中率还是流量命中率。大文件视频、安装包、图片资源对流量命中率影响很大,小接口、小图片、HTML对请求命中率影响更明显。源站压力更接近请求命中率,因为连接数、Nginx worker、PHP-FPM、Java线程池、数据库连接池,主要按请求数被打满。
为什么命中率下降后源站会被放大打穿
缓存未命中不是只多了一次源站请求
一个缓存MISS通常会触发一串动作:CDN节点向源站建连,源站Nginx接收请求,应用层处理,可能查Redis,可能查MySQL,可能访问对象存储,还可能做鉴权、签名、图片裁剪、压缩。用户侧只看到一个URL,源站侧可能是三四个内部服务在工作。
这里补充一点,动态页面如果没有做合理的边缘缓存,CDN更多只是转发代理。命中率低的时候,CDN节点并没有帮源站挡住多少请求,反而因为节点分布多,回源来源IP变多,源站日志看起来像“很多地方同时在打”。
热点资源过期会形成回源尖峰
最常见的是活动页、游戏更新包、直播封面图、App启动图这类资源。平时CDN命中率很高,但某个热点文件TTL同时过期,大量节点一起回源。源站不是慢慢增加压力,而是突然被几百个边缘节点同时请求。
如果CDN没有开启回源合并,或者源站没有做请求合并,同一个文件可能被多个节点重复拉取。大文件场景更明显,单个1GB安装包被几十个节点同时回源,源站出口带宽瞬间被占满,正常接口也会受影响。
缓存键配置错了,命中率会被打散
实际使用中发现,很多命中率下降不是CDN质量问题,而是Cache Key被参数打散了。比如:
/static/app.js?v=1001
/static/app.js?v=1002
/static/app.js?from=ios
/static/app.js?utm_source=ad
如果这些参数不影响文件内容,却参与了缓存键,每个URL都被CDN当成不同资源。用户访问越多,缓存越碎,节点越难命中。
类似问题还包括Cookie参与缓存、Accept-Language参与缓存、Header配置过多、URL大小写不统一、HTTP和HTTPS缓存分开、带鉴权参数的图片没有做规则归一化。看起来资源一样,CDN眼里不是一个对象。
哪些场景最容易把源站压力翻几倍
游戏更新包和补丁分发
游戏业务对CDN命中率特别敏感。客户端下载补丁时,用户行为很集中,资源体积大,峰值窗口短。命中率从98%掉到90%,源站带宽可能从几百Mbps冲到几Gbps。
如果源站本身只有100Mbps或200Mbps出口,很快就会出现回源慢、CDN节点拉取超时、用户下载速度下降。用户越重试,请求越多,源站越忙,形成恶性循环。
图片站和内容站的缩略图服务
图片业务常见问题是动态裁剪。比如同一张原图被请求成不同尺寸:
/img/abc.jpg?w=200
/img/abc.jpg?w=400
/img/abc.jpg?w=800
如果尺寸规则不收敛,CDN缓存对象数量会膨胀。有人随便传w=399、w=401,源站就要重新裁剪。CPU上升很快,磁盘缓存也容易被打爆。
这类业务建议固定尺寸档位,把参数归一化,源站生成后再让CDN缓存。不要把无限参数直接暴露给用户端。
电商、活动页、企业官网的HTML缓存
很多站点静态资源命中率很好,但HTML不敢缓存,所有首页、列表页都回源。访问量小时看不出来,广告投放或活动入口一开,源站应用层先被打满。
HTML不是不能缓存,而是要分页面类型。首页、频道页、活动页可以缓存10秒、30秒、60秒,配合主动刷新。短TTL也能挡掉大量瞬时请求。对源站来说,10秒缓存和不缓存差别非常大。
排查命中率低,别只盯着CDN控制台
源站日志里看MISS来源
CDN控制台能看到整体命中率,但要定位问题,源站Nginx日志更直接。建议日志里保留这些字段:
request_uri、status、body_bytes_sent、request_time、upstream_response_time、http_user_agent、http_referer、remote_addr、X-Forwarded-For、CDN回源Header。
重点看回源URL排名。很多时候前20个URL就能解释大部分回源流量。如果看到大量带随机参数的静态资源,基本就是缓存键或前端版本策略有问题。
区分状态码带来的回源
源站压力上升时,要把2xx、3xx、4xx、5xx拆开看。有些站命中率低,是因为404没有缓存。攻击者或爬虫扫不存在路径,CDN每次都回源,源站在处理大量无意义请求。
404、403、301、302是否缓存,要根据业务定。比如静态资源的404可以短时间缓存30秒到300秒,能明显减少异常扫描带来的回源。动态接口的错误码则要谨慎,避免缓存错误结果。
看回源带宽,也要看回源连接数
有些故障不是带宽满,而是连接数满。小文件MISS多时,源站出口带宽可能只有几十Mbps,但Nginx连接数、应用线程、TIME_WAIT数量已经很高。
如果源站到CDN节点之间链路质量不好,回源RTT高,连接占用时间变长,同样QPS下并发连接数会明显增加。海外源站服务国内用户时尤其明显,BGP、CN2、GIA线路差异会体现在回源稳定性上。
源站容量怎么估,不要按平均流量估
源站容量要按“CDN命中率下降后的回源峰值”估,不要按日均带宽估。日均数据很容易掩盖突发。
一个简单估算方式:
源站回源QPS = 用户总QPS × (1 - CDN请求命中率)
源站回源带宽 = 用户总带宽 × (1 - CDN流量命中率)
如果用户侧峰值是20000 QPS,正常命中率96%,源站大约800 QPS。命中率掉到85%,源站就是3000 QPS,接近3.75倍。再考虑CDN节点回源重试、源站慢响应、用户刷新重试,实际压力可能更高。
在购买源站服务器时,不能只看平时监控。源站至少要能抗住命中率异常下降时的一段时间,不然CDN还没来得及重新预热,源站已经先挂了。如果业务是游戏更新、企业官网活动页、海外访问国内或国内访问海外,可以看看129云的云服务器和大带宽服务器产品,像美国精品网-C型提供4C、8G、100Mbps峰值、三网精品线路,适合中小型海外源站;需要香港高防和较大入口防护的场景,可以看香港高防-C型,8C、8G、250Mbps峰值、200Gbps单机防御。需要确认线路和防护策略时,直接打客服热线400-9177118沟通会更快。
缓存规则里几个容易踩的配置
Cache-Control被应用覆盖
很多团队在CDN侧配了缓存时间,但源站返回了Cache-Control: no-store、private、max-age=0,CDN按源站Header执行,结果节点不缓存。排查时要直接curl源站和curl CDN域名,对比响应Header。
常见静态资源建议带明确版本号,然后设置较长缓存:
Cache-Control: public, max-age=31536000, immutable
HTML可以短缓存:
Cache-Control: public, max-age=30
接口类内容要看是否允许共享缓存,涉及用户态数据的接口不要随便缓存到公共CDN节点。
刷新太频繁会把节点缓存打空
有些发布系统每次上线都全站刷新,几万、几十万个URL一起清掉。刷新完成后,用户请求全部变成MISS,源站马上迎来一波回源洪峰。
更稳的做法是静态资源文件名带hash,新版本走新URL,旧资源继续留在CDN节点上。HTML做小范围刷新或短TTL。这样发布时不会把已有缓存全部清空。
预热不是摆设
大文件、热点图片、活动页上线前,预热很关键。CDN预热的意义是让节点提前回源拉资源,不等用户请求时再拉。尤其是游戏包、App安装包、视频文件,预热能把回源压力提前摊开。
预热也要控制速度。一次性提交大量大文件,源站同样会被预热任务打满。比较稳的是分批预热,观察源站带宽、连接数、磁盘IO,再继续下一批。
源站自身也要有抗回源能力
Nginx层做缓存和限速
CDN不是源站唯一防线。Nginx本地可以做proxy_cache、open_file_cache,对热点文件再挡一层。即使CDN MISS打到源站,源站Nginx也不一定每次都进应用。
对大文件下载,可以限制单连接速度,避免少量连接把出口带宽占满。对异常URL、随机参数、明显扫描路径,可以在Nginx层直接拦截或返回短缓存的错误码。
应用层减少动态生成
图片裁剪、报表导出、页面渲染这类CPU密集操作,不要每次请求都实时算。可以生成后落盘或写对象存储,再交给CDN缓存。
如果必须动态生成,也要做任务队列和并发限制。源站最怕无限制动态任务,被一波MISS拖死后,健康检查失败,CDN继续回源重试,故障范围会扩大。
数据库不要站在回源第一线
CDN命中率下降时,如果每个MISS都穿透到MySQL,数据库会比Web层更早出问题。热点数据要放Redis,本地缓存也可以用。活动配置、商品基础信息、游戏下载版本信息这类数据,更新频率低,没必要每次查库。
这里不是说所有内容都缓存,而是要把高频读、低频改的数据从数据库前面拿出来。CDN挡一层,源站Nginx挡一层,应用缓存挡一层,数据库才不会被用户峰值直接压住。
DDoS和低命中率叠在一起更麻烦
DDoS不一定都是大流量。有些HTTP Flood会专门打随机URL、随机参数,让CDN无法命中缓存。CDN节点收到请求后不断回源,源站看到的就是大量MISS。
这种情况下,单纯提高源站配置不一定有效。要在CDN或高防节点上做规则:参数白名单、URL频率限制、UA过滤、Referer校验、Bot策略、验证码或JS Challenge。对静态资源域名,参数能忽略就忽略,不要让攻击者用随机参数把缓存打散。
如果业务本身容易被打,比如游戏、支付入口、活动页、API网关,源站建议放在高防后面。香港高防这类节点适合需要低延迟又要防护的场景,129云香港高防-C型带200Gbps单机防御和高防IP,适合承接被攻击时的入口流量,源站再通过白名单只允许CDN或高防回源IP访问。
平时监控要盯哪些指标
CDN侧
请求命中率、流量命中率、回源QPS、回源带宽、回源状态码、回源失败率、各区域命中率、Top MISS URL,这些指标要放在同一个看板里。只看总命中率不够,区域异常经常被平均值盖住。
比如华东命中率95%,华南命中率95%,海外某区域命中率60%,总命中率可能还算正常,但海外源站回源链路已经很吃紧。
源站侧
Nginx活跃连接、请求耗时、upstream耗时、5xx数量、CPU steal、load average、磁盘await、网卡丢包、TCP重传、TIME_WAIT、应用线程池、Redis和MySQL连接数,都要跟CDN回源指标对齐看。
特别是upstream_response_time。如果CDN回源增加后,upstream耗时开始上升,说明应用层已经被拖慢。应用一慢,CDN等待时间变长,用户端超时重试,源站连接占用更久,压力继续放大。
线上处理时可以按这个顺序压住风险
先止住异常回源
看到命中率突然下降,不要急着重启服务。先看Top MISS URL。如果是随机参数,马上在CDN侧配置忽略无关参数,或者临时把异常参数请求拦掉。如果是404扫描,给静态资源404加短缓存。如果是某类接口被刷,先限频。
再保护源站入口
源站安全组或防火墙只放行CDN回源IP,避免用户绕过CDN直连源站。Nginx层加连接数限制、请求速率限制,对大文件回源做限速。必要时临时扩容源站带宽和实例,把最危险的峰值扛过去。
然后处理缓存策略
确认Cache-Control、Expires、Vary、Cookie、Query String缓存规则。静态资源尽量长缓存,HTML短缓存,动态接口按业务拆分。热点文件提前预热,发布系统避免全站刷新。
有一次处理下载站故障,用户侧带宽不到3Gbps,源站平时只回源100Mbps左右。上线新版本后发布系统全量刷新,命中率从97%掉到78%,源站出口1Gbps直接打满,Nginx开始大量499和504。后来把安装包改成hash文件名,发布只刷新入口JSON,安装包提前分批预热,源站回源峰值压到了200Mbps以内。
别把CDN当成无限缓冲层
CDN能挡住大量静态请求,但前提是缓存规则正确、资源可缓存、热点提前预热、源站能承受异常回源。命中率低的时候,源站压力翻几倍很正常;命中率从95%掉到80%,源站压力变成4倍,并不夸张。
源站配置、线路质量、防护能力也要跟业务峰值匹配。访问海外用户多的站点,源站线路要关注BGP、CN2、GIA等网络质量;攻击风险高的业务,要考虑高防IP和只允许CDN回源。服务器选型时别只看CPU和内存,带宽峰值、回源稳定性、防护能力、机房到用户区域的RTT都要一起看。
缓存命中率告警建议设得敏感一点,比如正常96%,掉到93%就提醒,掉到90%就进入故障关注。等源站CPU 100%、数据库连接打满、CDN回源失败率上升后再处理,已经晚了一拍。