API接口命中率低,绝大多数时候不是CDN厂商的锅,而是缓存键把本该复用的请求拆成了无数份,或者回源设置让缓存层根本“不敢缓存”,先把这两处捋顺,命中率通常能回到正常水位。
缓存命中率这件事,说到底是“同一份数据被重复请求了多少次,其中多少次被缓存层直接接住了”,接不住,无非两个原因:请求在缓存层眼里长得不一样,或者缓存层被明确告知别存,前者是缓存键,后者是回源设置。
缓存键:命中率的第一道闸门
缓存键到底在决定什么
缓存层判断“这是不是同一个请求”,靠的是一串键值,常见的组成是:请求路径 + 查询参数 + 部分请求头,键越细,能复用的请求越少;键越粗,越容易串数据,命中率低,往往是键细得没必要。
举个例子,同一个商品详情接口,前端每次带的时间戳、渠道标识、埋点参数都不一样:
GET /v1/goods/detail?id=1001&_t=1735689600&utm_source=app&trace_id=abc123
如果缓存键把整串查询参数都算进去,那么每个用户、每次刷新都是一次全新的键,缓存层每次都回源,可实际上,id=1001 才是决定返回内容的唯一变量。
三个最常见的缓存键设计错误
- 查询参数全量进键。 埋点参数、时间戳、随机数、SDK版本号全被算进去,键的基数瞬间爆炸,正确做法是在CDN或网关侧配置“忽略参数”列表,只保留业务参数。
- Cookie 整串进键。 只要站点往请求里塞了 Cookie,缓存键就变成一人一键,合理做法是把 Cookie 加入白名单,只保留真正影响返回内容的字段,其余一律忽略。
- Vary 头开得太宽。
Vary: User-Agent会把每个浏览器版本、每个App版本都拆成一个缓存副本,业内专家指出,Vary 应当尽量收敛到影响内容协商的少数几个头,Accept-Encoding。
自建缓存时,键怎么配
用 Nginx 做反向代理缓存的团队,重点看 proxy_cache_key 这一行,默认值通常包含 $scheme$proxy_host$request_uri,$request_uri 是带原始查询串的,参数顺序一变键就变,两种改法:
- 用
$uri$is_args$args配合参数排序,把?a=1&b=2和?b=2&a=1归一成同一个键。 - 干脆只取业务参数,
proxy_cache_key "$scheme$host$uri$arg_id";,其余参数不参与键计算。
配置改完记得 nginx -t 校验,再 nginx -s reload,改完用两次相同请求对打,看响应头里的 X-Cache-Status 是否从 MISS 变 HIT。
CDN缓存键配置错误导致回源率高怎么办
先从控制台把口径统一,不同厂商的命中率统计方式不完全一样,有的按请求数算,有的按流量算,有的把 304 也算命中,看板上的数字先别急着信,用响应头自己核一遍:X-Cache、Age、CF-Cache-Status 这类字段能直接告诉你这次请求走没走缓存。
控制台里的四个开关
- 忽略 URL 参数。 在缓存键设置里排除
utm_、_t、timestamp、trace_id、spm这类参数,保留业务必需项,这是见效最快的一步。 - Cookie 白名单。 默认“全部忽略”或“仅保留指定字段”,不要把登录态整串带进键。
- Vary 收敛。 关闭按 User-Agent 全量区分,只保留真正需要的协商头。
- 缓存键归一化。 开启参数排序、大小写统一,避免同一资源多种写法。
改完怎么验证
命令行就够用,连续发两次相同请求,第二次应当看到 Age 大于 0 且 X-Cache 为 HIT:
curl -sI 'https://api.example.com/v1/goods/detail?id=1001' | grep -iE 'age|x-cache|cache-control'
如果第二次仍是 MISS,问题多半不在键,而在回源响应头,往下看。
API接口缓存命中率低怎么排查:三步定位法
第一步:抓一条真实请求的完整键
打开浏览器开发者工具或抓包,把请求路径、全部查询参数、Cookie、关键请求头记下来,别只看路径,参数顺序和多余参数才是重灾区。
第二步:对比两次请求的键差异
同一资源,换个账号、换个入口、刷新一次,各抓一条,把参数列表并排一看,哪些字段在变、变的值是否真的影响返回内容,一目了然,变而不影响内容的,就是该从键里剔除的。
第三步:看回源响应头是否允许缓存
有些接口命中率低,是因为源站自己在“拒绝”缓存:
Cache-Control: no-store或private:缓存层不会存。Set-Cookie:很多CDN默认见到 Set-Cookie 就不缓存。Authorization请求头:默认不参与共享缓存。Vary::等于告诉缓存层别存。
排查命令:
curl -sI 'https://api.example.com/v1/goods/detail?id=1001' -H 'Cookie: session=xxx' | grep -iE 'cache-control|vary|set-cookie|age'
如果业务允许缓存,把 Cache-Control 改成 public, max-age=60, s-maxage=300 这类带 s-maxage 的写法,明确告诉共享缓存可以存多久。
回源设置里那几个“命中率杀手”
回源 HOST 与协议不一致
CDN 回源时用的 HOST 如果和缓存键里的 HOST 不一致,或者 HTTP 回源、HTTPS 缓存混用,容易造成同一资源被当成多份缓存,统一回源协议和回源 HOST,键才稳定。
回源请求头被改写
部分配置会在回源时追加 X-Forwarded-For、X-Real-IP 之类的头,如果这些头参与了缓存键计算,每个客户端IP都会生成一份缓存副本,命中率直接塌方,检查回源请求头配置,把非必要头从键里摘出去。
TTL 过短与缓存规则优先级
TTL 设得太短,缓存刚存下就过期,回源比例自然高,不同厂商的规则有优先级,路径规则、后缀规则、参数规则可能互相覆盖,配置时按“最具体优先”逐条核对。
| 配置项 | 常见错误值 | 更合理的做法 | 对命中率的影响 |
|---|---|---|---|
| 缓存 TTL | 5秒 | 按数据更新频率设 60–300 秒 | 明显提升 |
| 忽略参数 | 关闭 | 忽略埋点、时间戳类参数 | 明显提升 |
| Cookie | 全量参与键 | 白名单,仅保留必要字段 | 明显提升 |
| Vary | User-Agent | 仅 Accept-Encoding | 中等提升 |
| 回源 HOST | 与源站不一致 | 与缓存键 HOST 统一 | 减少重复副本 |
鉴权接口的缓存策略
带登录态的接口不是不能缓存,而是不能按人缓存,把用户维度从缓存键里剥出来,改用业务维度做键,例如商品ID、门店ID、地区编码,用户个性化部分放到边缘计算或前端拼装,公共数据走共享缓存,行业共识认为,接口缓存的粒度应当对齐数据本身的变更频率,而不是对齐请求来源。
API网关缓存命中率优化方案
分层缓存,别让一层扛所有
网关层缓存做短 TTL 的兜底,CDN 层缓存做长 TTL 的公共数据,源站前面再加一层本地缓存,请求从外到内逐层过滤,回源量自然下来,关键是三层的缓存键规则要一致,否则同一份数据在不同层各存一份,反而增加不一致风险。
缓存预热与软过期
热点数据在活动开始前主动预热,避免开抢瞬间全部穿透,软过期采用“过期后先返回旧值、同时异步回源”的策略,用户体验和回源压力都能兼顾,多数CDN厂商都提供预热接口,把热点 URL 列表批量提交即可。
盯住三个指标
- 缓存命中率:分路径、分参数维度看,别只看大盘。
- 回源 QPS:命中率提升后这个数应当同步下降。
- 回源带宽:据公开的CDN服务商技术文档,回源带宽费用通常按阶梯计费,回源量降下来,账单会直接体现。
收尾
命中率低不是一个玄学问题,它是一道“键是否一致、响应是否允许缓存”的判断题,把缓存键收敛到业务维度,把回源响应头改成明确允许缓存,大部分接口的回源比例都会回到合理区间。
Q&A:API接口命中率低相关问题
缓存键里加了用户ID,命中率是不是一定低?
不一定,但共享缓存场景下基本会低,按用户维度做键意味着缓存副本数等于用户数,只有单用户高频重复请求才可能回本,更常见的做法是把用户维度从键里拿掉,个性化数据单独走不缓存的接口,或者用边缘脚本拼装。
命中率上去了,但返回了别人的数据怎么办?
这是缓存键过粗的典型症状,通常发生在把 Cookie 或鉴权头从键里摘掉之后,补救思路是确认哪些字段真正影响返回内容,把它们加回键里,同时对响应加 Vary 或 Cache-Control: private,改完务必用两个不同身份的账号交叉验证,确认不串数据再放量。
回源带宽费用高怎么降低?
先按上面的方法把命中率提上去,回源量是费用的直接来源,其次检查 TTL 是否过短、预热是否到位、是否存在大量参数化 URL 导致缓存碎片,最后核对计费口径,部分厂商对回源请求数和回源流量分别计费,命中率提升后两项都会下降。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/794094.html


