直接看日志里的$upstream_cache_status就能精准判断缓存行为,其六种状态(hit/miss/expired/bypass/stale/revalidated)需分清,统计时应以六种状态之和为分母,避免仅用hit/(hit+miss)导致失真。

直接看访问日志里的 $upstream_cache_status,就能知道每个请求是否真走了缓存、为什么没命中、有没有悄悄回源——不是靠猜,而是靠每条日志里明确记录的状态。
确保日志里真有缓存状态字段
默认 access_log 不包含缓存行为信息。必须在 http 块中明确定义带 $upstream_cache_status 的日志格式:
- 添加日志格式:
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_pass 的 location 中启用该格式:
access_log /var/log/nginx/api_cache.log cache_log; - 避免把
log_format写在server块内却把access_log放在http块,作用域不匹配会导致字段为空或显示为- - 确认该 location 真启用了
proxy_cache my_cache,且my_cache已在http块中用proxy_cache_path正确定义
区分六种状态,别只算 HIT/MISS
$upstream_cache_status 有六种取值,每种代表不同缓存逻辑环节,混在一起统计会严重失真:
- HIT:缓存直接返回,理想状态
- MISS:首次访问或 key 不匹配(如 URL 含未清洗的 utm 参数),需检查缓存键设计
-
EXPIRED:缓存存在但已过期,强制回源并刷新,通常因
proxy_cache_valid设置过短 -
BYPASS:被
proxy_cache_bypass规则跳过(如含Cookie、Cache-Control: no-cache或特定参数),说明请求根本没进缓存池 - STALE:后端不可用时返回旧内容,反映上游稳定性问题,不是缓存配置问题
- REVALIDATED:缓存未过期但后台发起校验,用户拿到的是新鲜响应,属后台刷新机制
用命令行快速定位回源与命中问题
不用上复杂平台,几条 shell 命令就能暴露关键瓶颈:
- 统计各状态分布:
awk '{print $11}' /var/log/nginx/api_cache.log | sort | uniq -c | sort -nr(假设状态是第11列) - 计算基础命中率(仅 HIT 占比):
awk '$11=="HIT"{hit++} END{print "HIT rate:", hit/NR*100 "%"}' /var/log/nginx/api_cache.log - 识别高频回源请求:
grep -E "(MISS|EXPIRED|BYPASS|REVALIDATED)" /var/log/nginx/api_cache.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -10 - 查 BYPASS 共性:
grep "BYPASS" /var/log/nginx/api_cache.log | head -20 | awk -F'"' '{print $6}',看是否集中出现在带Authorization、Cookie或 POST 请求中
排除干扰项,让统计更贴近真实回源压力
以下情况会让日志中的状态或流量数据失真,分析前建议过滤:
- 排除
status == 304和request_method == "HEAD"的行,它们的$body_bytes_sent为 0,但可能也是 HIT,若评估带宽节省效果应剔除 - 若关注“实际回源次数”,把
EXPIRED、MISS、BYPASS、REVALIDATED都计入分母,只将HIT和STALE视为“未触发新回源” - 注意 gzip 压缩影响:若开启
gzip on,$body_bytes_sent是压缩后大小,计算带宽节省率时需统一口径 - 后端返回
Set-Cookie或Vary: *会导致 Nginx 默认跳过缓存,此时日志中大量-或BYPASS,需加proxy_ignore_headers Set-Cookie Vary并确认后端可信











