一、缓存命中率低的典型问题与根因

我之前帮一个本地电商网站做技术优化时,碰到过很典型的CDN问题:他们接入阿里云CDN快半年,后台监控显示缓存命中率仅38%,源站带宽每月支出超5万,比预期多了一倍。更麻烦的是,静态商品图片、CSS样式表几乎每次请求都要回源拉取,动态接口偶尔还会出现缓存旧数据的情况——比如用户看完某商品的活动价,刷新页面又回到原价,差点造成客诉。 经过排查,这个问题的核心不是CDN不够好用,而是团队没搞对CDN的缓存逻辑,踩了两个常见坑。

1.1 业务场景的真实痛点

这个电商网站的日活大概10万,移动端占比70%,用户反馈最多的就是首页加载慢、商品页图片刷不出来。原来的CDN配置里,所有请求都被归为“动态内容”,导致CDN完全不缓存静态资源,源站相当于24小时被CDN的请求占满;动态接口的缓存规则没设对,少量用户的个性化请求被误缓存,才出现了价格混乱的问题。

1.2 根因拆解

问题出在三点:一是没有区分动态与静态内容,CDN默认把所有请求当动态处理,不会做缓存;二是源站返回的缓存头(Cache-Control)不规范,静态资源没设足够的缓存时间,动态资源没设置不缓存;三是没有做内容分区,静态和动态的缓存规则混在一起,互相干扰,才导致命中率上不去。

二、动态与静态内容分区缓存的落地步骤

解决这个问题的核心逻辑是:把静态资源交给CDN缓存,动态资源完全不缓存,中间再配合合理的过期时间策略,这样既能减少源站压力,又能保证动态内容的实时性。

2.1 先明确静态和动态的边界

不用搞复杂的分类,按两个维度分就行:一类是很少变化或不会变的内容,比如商品主图、logo、CSS样式表、JS脚本、字体文件,这些改一次需要发版,不会随时动;另一类是每次请求可能不一样的内容,比如用户订单、实时价格、个性化推荐接口路径带/api//user/,这些是动态内容。

2.2 配置CDN的分区规则

大部分CDN都支持按文件后缀或路径设置缓存规则,以常用的阿里云CDN为例,操作很简单:登录控制台→进入对应域名→找到【缓存规则】→添加两类规则:

  1. 静态资源规则:匹配*.jpg|*.png|*.css|*.js等后缀,设置缓存时间;
  2. 动态资源规则:匹配/api/*/user/*路径,设置不缓存。 不用纠结太细的分类,按路径或后缀分已经足够支撑命中率达标。

2.3 Cache-Control与过期时间调优

这一步是核心,相当于给每个资源贴“缓存标签”,告诉CDN和浏览器这个资源该怎么存、存多久。我们用Nginx作为源站(技术栈统一为Nginx),具体配置如下,注释里会说明每个部分的作用:

# 匹配所有静态资源后缀,设置公共缓存规则
location ~* \.(jpg|jpeg|png|gif|css|js|woff|svg)$ {
    # 过期时间设为30天,意思是CDN和浏览器可以把这个资源存30天
    expires 30d;
    # Cache-Control头:public表示允许CDN缓存,max-age是秒数(30天=2592000秒)
    add_header Cache-Control "public, max-age=2592000";
}

# 匹配所有动态接口路径,设置不缓存规则
location ~* ^/api/ {
    # 过期时间设为-1,代表资源立即过期,完全不缓存
    expires -1;
    # Cache-Control头:no-cache+no-store,意思是任何情况都不存这个资源
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    # 加上Vary头,避免因为Cookie等差异导致的缓存混乱
    add_header Vary "Cookie, Accept-Encoding";
}

这里补充一个小知识点:Cache-Control的几个常用值,不用背,理解就行:public是给所有人用的公共缓存,适合静态资源;private只给单个用户的浏览器用,动态内容才用;no-store是绝对不缓存,适合实时性要求极高的接口。

三、落地后的收益与需规避的坑

3.1 调优后的实际效果

配置完规则后,CDN的监控数据立马有了变化:缓存命中率从38%升到了93%,源站带宽从每天120G降到25G,每月带宽支出从5万降到1.1万;首页加载时间从2.1秒降到0.8秒,移动端加载时间从3.2秒降到1.1秒,用户反馈的“加载慢”问题几乎消失,也没再出现动态内容缓存旧数据的情况。

3.2 优缺点分析

这套方案的优点很明显:一是命中率提升幅度大,能大幅降低源站压力和带宽成本;二是配置简单,不用改业务代码,只要调整CDN规则就行;三是适配所有支持缓存规则的CDN,不管是阿里云、腾讯云还是国外的Cloudflare都能用。 缺点也存在:一是静态资源更新需要手动刷新CDN缓存,比如改了logo或商品图,得去CDN控制台清对应文件,不然用户会看到旧内容;二是如果静态资源没加版本号,改了JS或CSS后,用户还是会用旧的缓存,可能导致功能异常。

3.3 落地时的注意事项

第一个要注意的是静态资源加版本号:比如把app.js改成app.v1.2.3.js,每次更新后文件名变了,CDN就会缓存新的,旧的缓存会自动过期,不用手动刷新;第二个要测试配置是否正确:用curl命令检查缓存头是否生效,比如:

# 检查静态资源的缓存头
curl -I https://example.com/static/logo.png
# 应该返回类似 Cache-Control: public, max-age=2592000 的内容

# 检查动态接口的缓存头
curl -I https://example.com/api/user/info
# 应该返回类似 Cache-Control: no-cache, no-store 的内容

第三个要避免把大文件设为永久缓存,比如超过10MB的视频,设7天缓存就行,不然会占用CDN的存储成本。

四、总结

CDN缓存命中率低的核心不是工具不好,而是没搞懂“缓存的边界”——静态内容该让CDN存,动态内容该让CDN完全不碰,再配合合理的过期时间规则,就能快速解决问题。这套方案适合绝大部分中小网站,不管是电商、博客还是资讯站,只要区分好静态和动态内容,调整几个CDN规则就能落地,既能降低成本,又能提升用户体验,是性价比极高的优化方法。