直接查看 $upstream_cache_status 可精准判断缓存行为,其六种状态(hit/miss/expired/bypass/stale/revalidated)需分清,统计时应按优化目标选择对应分母,避免仅用 hit/(hit+miss) 导致命中率失真。

直接看日志里的 $upstream_cache_status,就能知道每个请求在 Nginx 缓存层到底发生了什么——不是简单问“有没有缓存”,而是确认它走的是 HIT、MISS 还是被 BYPASS 绕开了。命中率统计失真的主因,往往是把不同逻辑状态混在一起算,比如只用 HIT/(HIT+MISS),却忽略了 EXPIRED、STALE 和 BYPASS 实际都代表缓存机制未按预期生效。
确保状态字段真实、稳定写入日志
如果日志里这个字段大量是 - 或空,说明变量根本没采集成功,所有后续分析都是空中楼阁。常见原因有:
- 对应 location 没配
proxy_cache my_cache(必须引用 http 块中已定义的 keys_zone) - 后端响应带
Cache-Control: no-store、Set-Cookie或Vary: *,Nginx 默认跳过缓存 - 误在静态文件 location 里用
$upstream_cache_status(它只对proxy_pass有效;静态直出应改用$sent_http_x_cache等自定义头) -
log_format定义在 server 块内,但access_log写在 http 块,作用域不匹配
推荐统一在 http 块定义日志格式,并为反向代理流量单独切分日志文件:
log_format cache_log '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $upstream_cache_status $upstream_response_time $request_time';
再在 proxy location 中启用:
location /api/ {<br> proxy_pass http://backend;<br> proxy_cache api_cache;<br> access_log /var/log/nginx/api_cache.log cache_log;<br>}
六种状态含义必须分清,不能笼统归类
$upstream_cache_status 不是开关,而是缓存生命周期各环节的快照。每种取值指向不同配置或运行问题:
- HIT:缓存直接返回,理想状态
- MISS:首次访问或缓存未建立,正常但占比过高说明 key 设计太细或资源冷启动频繁
-
EXPIRED:缓存存在但已过期,强制回源,通常因
proxy_cache_valid设置过短 -
BYPASS:被
proxy_cache_bypass规则触发(如用户带特定 cookie 或请求头含Cache-Control: no-cache),需检查是否误配绕过条件 -
STALE:后端不可达时返回旧内容,说明
proxy_cache_use_stale生效,但后端稳定性值得排查 - REVALIDATED:缓存未过期但后台发起校验,用户拿到的是新鲜响应,属后台刷新机制
统计时,分母应覆盖全部六类有效状态,而非仅 HIT+MISS。
按目标选分母,避免一刀切公式
“命中率”没有唯一标准,要根据优化目标决定怎么算:
- 评估缓存机制整体利用率:分母 = 所有非 - 的状态(HIT + MISS + EXPIRED + BYPASS + STALE + REVALIDATED)
- 关注用户实际获得新鲜响应的比例:排除 BYPASS(根本没走缓存)和 STALE(返回过期内容),分母 = HIT + MISS + EXPIRED + REVALIDATED
- 评估回源压力:把 EXPIRED、MISS、BYPASS、REVALIDATED 都算作“需回源”,只将 HIT 和 STALE 视为“未触发新回源”
例如,若目标是降低后端负载,那 HIT + STALE 就是真正“免回源”的请求,其余都应计入压力来源。
用 awk 快速验证与日常统计
重启 Nginx 后,发两次相同请求(首请求应为 MISS,次请求应为 HIT),检查日志末尾是否出现类似 HIT 0.003 或 EXPIRED 0.217 的字段。
用以下命令快速估算最近 1 小时缓存整体利用率(HIT 占所有有效请求比例):
awk '$12=="HIT" {hit++} $12 ~ /^(MISS|EXPIRED|BYPASS|STALE|REVALIDATED)$/ {total++} END{printf "%.2f%\n", hit/total*100}' /var/log/nginx/api_cache.log
注意:字段位置(这里是 $12)取决于你的 log_format 定义,建议用 awk '{print NF; exit}' 先确认总列数。











