ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CDN缓存规则设计:如何平衡命中率与内容实时性

CDN缓存规则设计:如何平衡命中率与内容实时性 1. 项目概述先别急着调参数把这对矛盾看透做 CDN 缓存规则设计的朋友基本都体会过这种感觉规则调松一点命中率立刻往下掉回源量蹭蹭往上涨账单肉眼可见地变厚规则调紧一点命中率倒是上去了可业务方总抱怨线上看到的数据还是昨天的。这个场景几乎每个公司在接入 CDN、优化成本时都会撞上区别只是有的人靠经验一点点磨有的人能快速找到一套平衡方法。这篇文章要聊的核心问题就是“缓存命中率”和“内容实时性”这对天生的冤家怎么调和。大多数文章只会告诉你要折中但折中的依据是什么、不同业务怎么选、参数到底怎么给很少有人说清楚。我会把自己实际做 CDN 规则设计的思路、配置细节、踩过的坑和排查方法完整展开适合三类人看一是刚接手 CDN 配置的运维工程师二是被业务方反复催“为什么内容不是最新”的后端开发三是想省回源带宽成本的技术负责人。先说结论平衡的关键不在“找出一个完美 TTL”而在于把内容分类、把实时性需求量化、再组合 TTL 校验头 版本号这类手段去匹配。把这个逻辑搞明白再看任何一家云 CDN 的控制台你都不会慌。2. 核心矛盾拆解命中率和实时性到底在争什么2.1 缓存命中率是什么为什么大家都想让它越高越好缓存命中率是一个 CDN 节点在收到用户请求时能直接从本地缓存返回响应的比例。命中率 90% 意味着 10 个请求里只有一个需要穿透到源站拿数据。剩下那个请求要经历完整的网络往返、源站处理逻辑、数据库查询时间消耗可能是命中请求的十倍以上。从成本角度更好理解云 CDN 的计费大头通常是回源流量回源一次不仅产生 CDN 源站间的带宽成本还要占用源站的并发连接和计算资源。一个日请求量千万级的站点命中率每提 1%可能每天就少了几万次回源。更关键的是回源请求一旦涌入源站响应变慢最终影响的是所有用户——哪怕缓存命中的那部分也快不起来因为源站处理不过来。所以大部分公司对命中率都有 KPI常见线下 CDN 的命中去到 95% 以上才觉得“正常”。但我见过太多人为了把命中率从 90% 提到 98%把 TTL 拉得极长最后造成的内容陈旧问题比省下的钱还麻烦。这就是盲目追求单一指标的危险。2.2 内容实时性换句话说是“业务能忍多久的旧数据”实时性并不是指物理意义上的立刻而是“业务容忍窗口”。新闻资讯站的首页推荐位用户希望刷新后一分钟内看到新稿电商的商品库存用户加购结算时看到的库存必须是当前可用的而一个背景图片或公共 JS 库哪怕滞后一周也完全无感。把“实时性”翻译成“业务容忍窗口”是缓存设计的第一步。你不需要把每个内容都做到秒级更新只需要让实际生效时间落在业务可接受的范围内。很多时候业务方说“必须实时”聊深了你才发现真正的要求只是“改完配置后 5 分钟内生效”而不是“每次请求都保证最新”。2.3 一切缓存策略的本质用“可容忍的旧”换“更快的读”我用一个生活化的比喻帮团队新同学理解这件事。缓存就像餐厅提前备好的预制菜和现炒的现场预制菜出餐快、翻台率高但客人等久了会质疑“这菜是不是早就炒好的”现炒的锅气足、新鲜但高峰期出餐慢可能就留不住客人。餐厅不能全部只做预制菜也不能全部只做现炒聪明的做法是把菜单分类招牌菜提前备料但不提前出菜普通菜提前做好的半成品真正“点完就要”的饮品走最快通道。CDN 缓存规则设计完全同理。把内容按“新鲜度敏感度”分类给每一类不同的策略而不是所有 URL 一套 TTL 走天下。这套分类逻辑比背一万个参数配置都重要。3. 规则设计的第一层先把内容按可缓存性分类3.1 五个维度判断一个 URL 能不能缓存、适合怎么缓存我做缓存规则巡检时不会先看控制台而是先拉一份接口清单按下面五个维度挨个过内容是否和登录用户绑定带个人信息的接口如“我的订单”“购物车详情”一般不应在 CDN 层缓存除非接受所有用户看到同一份响应。内容更新频率和更新方式是定时发布、人工修改还是用户行为触发的实时变更更新是“事件驱动”还是“轮询驱动”决定了失效方式。内容是否受参数影响同一个路径下加个?fromapp参数内容就不同那缓存键设计就要注意参数是否忽略。响应是否和地域/设备强相关选了城市节点的服务同一个 URL 在不同地域返回不同内容CDN 缓存键没有做地域区分就会串。首屏关键程度首屏接口挂了或旧了用户直接退出非关键接口如推荐位、热榜旧几秒无所谓。一个典型的坏例子很多团队把带用户 token 的接口也一股脑强制缓存结果 A 用户请求完B 用户命中缓存看到了 A 的数据。这种事故不是实时性问题是缓存设计底线问题。遇到带Set-Cookie、Authorization的请求默认就应该跳过 CDN 缓存除非你有完整的隔离方案。3.2 反向匹配先把绝对不缓存的名单写出来合理的设计顺序其实是反着的不是先想“哪些能缓存”而是先定“哪些绝对不能缓存”。登录态接口、支付金额接口、验证码、写操作的回执、实时库存扣减接口这些是硬名单直接跳过 CDN 或者用很短的 TTL 不缓存策略。硬名单之外的内容再按“时效敏感度”分三档。我常用的分法如下类别示例合理 TTL失效策略静态资产JS/CSS/图片/字体7~30 天版本号更新 主动 purge 旧版本低频变更数据文章详情、公告、类目信息5~60 分钟发布后主动刷新或短 TTL高频变更数据热点榜、库存、价格5~30 秒短 TTL stale-while-revalidate这个表不是公式是起跑线。每类还要结合具体业务再调。3.3 从业务容忍度反推目标先定“可接受的延迟窗口”分类定完后第二步是和业务方对齐“延迟窗口”。所谓延迟窗口就是一个内容变更后最迟多少秒内被 CDN 节点感知并更新。这个值不是拍脑袋建议用一个简单的公式延迟窗口 内容变更频率的 1/3 ~ 1/2 周期如果一条新闻的平均编辑周期是 10 分钟那 2~3 分钟的延迟窗口完全够用一个商品价格每 5 分钟可能有调整那延迟窗口就压到 1~2 分钟秒杀库存这种秒级变动的延迟窗口基本就是 5 秒以内甚至不进缓存。把延迟窗口定出来TTL 就有据可依了。注意这里有个很多人会忽略的点TTL 不等于延迟窗口。TTL 是“从缓存建立开始算的存活时间”取决于请求什么时候到达节点。一个请求刚好在 TTL 快到期时访问那新旧内容的切换点其实是随机的。要做更可控的实时性光靠缩短 TTL 不够得配合主动失效机制这部分后面会展开。4. 实操落地TTL、校验头、版本号三板斧怎么配合4.1 TTL 参数选型从源站响应头到 CDN 控制台现在主流的做法不是只在 CDN 控制台配活路 TTL而是在源站响应头里把语义写清楚让 CDN 读取并遵循。这样做最大的好处是源站可以针对不同接口灵活返回不同的缓存策略不用在 CDN 控制台堆一堆 rule。一个标准的响应头配置示例Cache-Control: max-age60, s-maxage30, stale-while-revalidate120这里的max-age控制浏览器缓存s-maxage专门控制 CDN 这类共享缓存stale-while-revalidate是允许在后台更新期间短暂返回旧内容。三者的关系我用大白话解释浏览器自己的小冰箱最多存 60 秒CDN 的大仓库只存 30 秒万一大仓库过期了可以先拿旧货给用户同时让供应商后台补新品最多多等 120 秒。实际配置时有几个容易踩的坑只配max-age不配s-maxageCDN 和浏览器用的同一个缓存时间用户端反而变成最长缓存方改内容后浏览器半天不拉新。private和public不要混用Cache-Control: private告诉共享缓存不能存很多 CDN 严格遵守。有些库自动加的Pragma: no-cache会把 CDN 整懵建议统一清理。4.2 ETag 与 Last-Modified让回源校验成本降到最低如果你把 TTL 设得很短比如 5 秒实时性确实有了但每个用户请求第一次到达节点时都会回源会放大源站压力。这时候就要启用条件请求校验机制。CDN 节点缓存过期后不会直接重新拉全量数据而是带着之前缓存的ETag或Last-Modified值发一个条件请求到源站。源站发现内容没变返回一个几乎没有 body 的304 Not ModifiedCDN 节点拿到 304 后把 TTL 重置继续用旧内容。这比“过期就全量回源”效率高得多。ETag 的实际价值在动态接口上体现得很明显。一个交易列表接口 10 秒过期一次但源数据每分钟才变一次稳妥情况下 5 次校验里会命中 4 次 304这 4 次几乎不消耗源站核心逻辑。需要注意ETag 的粒度不要设到每次响应都不同。有些框架默认用“响应体 hash”当 ETag导致条件请求每次都是内容变化、必须全量回源等于缓存形同虚设。可以改成按数据最后修改时间生成 ETag。4.3 版本号和内容寻址静态资源缓存的不二法门对于 JS、CSS、图片这类静态资源最有效的平衡方法根本不是调 TTL而是“版本哈希 永久缓存”。把文件名变成app.8f3d2a.js这样内容一变文件名就变新 URL 自然走去源站拉新文件旧 URL 留在 CDN 上继续被命中。组合起来就是Cache-Control: max-age31536000, immutable其中immutable告诉浏览器这一年里都不要来问“这文件变没变”省掉一堆预检请求。版本号一变新 URL 首次请求自动回源CDN 也会自动缓存新文件不需要主动 purge。这个思路还可以延伸到 API 层。一些公司对详情接口做/v1/goods/{id}/detail_v{版本号}.json的寻址业务更新时换新版本号这样既能长期缓存又能保证内容精准最新。代价是源站要多存几份旧版本数据不过通常很值得。5. 典型场景实操三套规则组合参考5.1 新闻资讯类TPC 10 秒级时效同时命中率过 90%新闻站最怕用户看到旧闻。我给一个典型新闻站的建议组合是资讯详情 HTMLs-maxage30ETag 校验首页接口s-maxage5图片等静态资源至少 7 天。同时发布系统在编辑点击发布后调用 CDN 的 purge 接口主动清除受影响 URL。这里有个细节主动 purge 是要花钱的很多云厂商按条计费。遇到大促、突发新闻几百条 URL 同时 purge 也没多少钱但发布会时加错参数 purge 成根路径/可能导致整个站点缓存瞬间清空、回源风暴。我的经验是 purge 请求一定要有白名单控制并且限定路径前缀线上环境拒绝手输一整个根路径。5.2 电商与价格库存秒级新鲜度不能牺牲可用性电商的库存查询接口是出了名的敏感用户加购时的可用库存必须和库里当前值一致否则会出现“下单成功、出库没货”的客诉。但电商大促时 QPS 极高全量打到源站也不现实。折中方案是非关键接口如商品详情用短 TTL 版本号库存接口不进 CDN 或 TTL 压到 3~5 秒。注意库存接口如果设置了 SWR一旦后台回源慢CDN 会先返回旧库存给用户这在大促时可能造成超卖。所以这类接口我宁愿让它多回源也不要让它 SWR。更极端的做法是库存请求不带缓存但在边缘节点做“合并回源”同一个商品 ID 的多个用户请求在 CDN 节点合并成一两个源站请求其余等待同一份响应。这个能力一部分云厂商叫“回源合并”也叫“缓存回源折叠”能有效扛住热点商品的高并发。5.3 大促秒杀与实时榜单直接绕过常规缓存碰到真正的实时性压倒一切的场景合理的做法不是把 TTL 调到 1 秒而是直接不缓存。比如秒杀商品详情页的库存展示我给它的配置是Cache-Control: no-store并加上一个时间戳 query让浏览器侧也强制拉新。但这会带来另一个问题不缓存意味着每个请求都到源站热点商品的查询会瞬间冲垮源站。所以秒杀场景通常还要配合服务端限流、库存预扣消息队列等手段CDN 只是链路的一环不是救世主。我见过不少团队在 CDN 配置上折腾了很久最后发现源站数据库查询慢才是根因。6. 常见问题排查与避坑指南6.1 都已经 purge 了为什么用户还看到旧内容这是被问到最多的一个问题。现象是控制台显示 purge 成功拿 curl 测试也返回新内容但用户普遍反馈还是旧页面。排查方向按顺序走先看浏览器缓存max-age没设或设得长浏览器直接本地复用了页面根本没发请求到 CDN。再看 CDN 节点是否多地域purge 消息有些厂商异步下发跨区域节点生效时间不同需要等 1~2 分钟。运营商 DNS 和本地缓存部分移动端的网络代理层也会缓存这在公共 WiFi 环境特别常见。排查时用不带缓存的关键请求头做测试最靠谱比如给 URL 加个随机参数如?timestampxxx传到 CDN 节点后看响应头里的X-Cache字段是HIT还是MISS。HIT 说明是旧缓存MISS 说明已经走到源站。6.2 命中率 99%可业务还是一卡一卡的命中率极高但体验差问题一般不在“缓存不命中”而在“命中和不命中的比例失衡”。比如 99% 的请求都命中了一个 5 分钟 TTL 的热点接口剩下 1% 恰好全是数据库深度查询就会拉长整体响应时间。这时候先别怀疑 CDN 规则打开监控看“回源响应时延”和“回源错误的分布”。如果回源平均 500ms那不命中的原因多半是源站本身有慢 SQL 或上游依赖超时。一个排查技巧在 CDN 日志里找出所有MISS请求的 URL按域名和路径聚合通常能精准定位“拖后腿”的几个接口。6.3 缓存击穿和惊群过期热点引发的回源风暴和 Redis 场景一样CDN 也会遇到缓存击穿某个热点 key 过期瞬间大量请求同时穿透到源站导致回源请求数瞬间飙高可能拖垮源站。表现是源站监控出现尖刺而 CDN 命中率在那一刻也掉到谷底。对策有几个层次第一层是 TTL 错峰同一个热点资源让不同节点保留不同的过期时间偏移第二层是打开 CDN 的“回源合并”功能让同时过期的大量请求在节点层合并为一两个源站请求第三层是对核心资源做主动预热在业务高峰期前把大流量内容提前推到主要节点。6.4 实战排查工具与速查表日常排查缓存问题我固定的一套动作是curl -I看响应头确认Cache-Control、ETag、Age等字段。换不同边缘节点测试确认问题是否局限在某个节点。去 CDN 日志里查HIT/MISS与回源耗时而不是只看面板指标。用同一个 URL 连续请求多次观察 Age 字段变化趋势判断 TTL 是否被重置。这里列一个速查表方便按症状快速定位症状第一怀疑对象优先操作改了内容用户看不到浏览器缓存 / CDN purge 未生效加参数测试看 X-Cache 字段命中率低动态接口无缓存策略给接口加 s-maxage开启条件请求命中率高但慢回源时延大 / 源站慢看回源日志做慢查询治理高峰期回源尖刺热点 key 过期开启回源合并加主动预热用户数据串号缓存了带用户态内容检查缓存键排除登录请求7. 进阶方法论用架构思维替代规则堆叠7.1 让数据“主动来找 CDN”而不是让 CDN 反复来问短 TTL 本质上是一种“轮询”反复回源确认数据变没变。更省的做法是版本号和主动失效结合业务方知道内容变了直接调 CDN 的 API 把旧缓存作废同时推送一份新内容到源站。这样就算 TTL 设得很长也能做到近乎实时。这个概念不复杂落地却需要一点工程改造。源站发布流程要接入一个发布中心发布后走消息队列触发 CDN purge同时记录当前内容版本号CDN 节点缓存内容时顺带缓存版本号每次请求到了先比较本地和源站的版本头。很多中大型团队在自建 CDN 时都会往这个方向优化。7.2 用“分片缓存”处理高频变化空间的实时性对于首页或者热点榜单这种集合型内容全量缓存代价太大每次都全量回源又浪费。我的做法是把大响应切成小片段骨架 HTML 长缓存里面的热点数据块走短缓存或者客户端异步加载。举例来说一个资讯首页可以分为导航栏、列表、右侧推荐位三块。导航栏一个月不变用长 TTL 缓存列表每分钟更新单独一个接口走短 TTL推荐位个性化不缓存。用户浏览器端用 async 请求把几块拼接起来。坏处是实现复杂了一些但 CDN 命中率和用户新鲜度体验能同时拉满。7.3 监控指标要看哪几个别被面板带偏缓存监控最忌讳“命中率一低就调 TTL”。我平时固定盯的四个指标整体命中率、按 URL 聚合的 MISS 分布、回源时延的 P95、purge 成功后的首次回源时延。这四个指标能定位出问题在“配置不合理”还是“源站慢”还是“推送链路有 bug”。再有别忽视“边缘命中率”和“用户侧命中率”的差异。边缘命中指 CDN 节点上有缓存用户侧命中指浏览器缓存直接命中。用户侧命中率高了CDN 层面看到的请求量自然就低整体成本又省一层。8. 个人经验从“总是折中”到“设计分层”的转变我把这个过程浓缩成三句话内容分类先行容忍窗口量化机制组合落地。纯粹的“折中”是非常模糊的体验看到业务方喊实时性就调短 TTL看到命中率下降就调长 TTL来回折腾只会让大家对你的专业度打问号。我现在接手一个新站点的缓存优化第一周不碰任何配置只出一份内容分类表和延迟窗口对齐表跟后端、产品、运营各确认一遍。第二周才去配置生效机制把 TTL、ETag、版本号、主动失效四类工具按表组合下去。第三周看监控调整参数。这样走下来绝大多数业务方的实时性诉求和你的节省成本目标在同一个架构里就自洽了根本不需要三天两头去“平滑折中”。最后分享一个小技巧配置完成后每季度做一次“缓存规则清单”审计看看是否有新接口漏配、是否有不再需要的长 TTL、是否有人直接绕过规则强加了缓存参数。CDN 规则不是一个静止的配置文件它会随着业务迭代慢慢腐烂定期清理和解释每一条规则的缘由是维护这个系统的长期修行。
返回列表