Nginx 从入门到精通 / 08 - 缓存机制 / Caching
缓存机制 / Caching
🟢 基础 / Basics — 最简单的缓存配置
启用代理缓存
http {
# 1. 定义缓存路径和参数
proxy_cache_path /var/cache/nginx
levels=1:2 # 目录层级:/a/ab/abcdef123456
keys_zone=my_cache:10m # 共享内存区域名称和大小
max_size=1g # 缓存最大磁盘空间
inactive=7d # 7 天未访问的缓存自动删除
use_temp_path=off; # 直接写入缓存目录
server {
location / {
proxy_pass http://backend;
# 2. 启用缓存
proxy_cache my_cache;
proxy_cache_valid 200 302 10m; # 200/302 缓存 10 分钟
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
proxy_cache_key $scheme$host$request_uri; # 缓存键
}
}
}
缓存工作流程
请求 /api/products:
第 1 次请求(缓存未命中):
Client → Nginx → Backend → 响应 → Nginx(写入缓存)→ Client
↑ MISS
第 2 次请求(缓存命中):
Client → Nginx → 直接返回缓存 → Client
↑ HIT(不访问后端)
缓存过期后:
Client → Nginx → 缓存过期 → Backend → 更新缓存 → Client
↑ EXPIRED / REVALIDATED
查看缓存状态
# 添加缓存状态 Header(调试用)
add_header X-Cache-Status $upstream_cache_status always;
$upstream_cache_status 取值:
MISS — 未命中缓存
HIT — 命中缓存
EXPIRED — 缓存已过期,已向后端重新获取
STALE — 缓存过期但后端不可用,返回过期缓存
BYPASS — 跳过缓存
REVALIDATED — 使用条件请求验证缓存仍有效
UPDATING — 缓存正在后台更新
🟡 进阶 / Intermediate — 生产缓存策略
完整缓存配置
http {
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=main_cache:50m
max_size=10g
inactive=30d
use_temp_path=off;
server {
listen 443 ssl;
server_name example.com;
# 静态资源缓存(CSS/JS/图片)
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
proxy_pass http://backend;
proxy_cache main_cache;
proxy_cache_valid 200 30d;
proxy_cache_key $uri;
add_header X-Cache-Status $upstream_cache_status;
# 浏览器缓存
expires 30d;
add_header Cache-Control "public, immutable";
}
# API 缓存(仅 GET 请求)
location /api/ {
proxy_pass http://backend;
proxy_cache main_cache;
proxy_cache_valid 200 5m;
proxy_cache_methods GET HEAD; # 只缓存 GET/HEAD
proxy_cache_key $scheme$host$uri$is_args$args;
# 不缓存带 Cookie 或认证头的请求
proxy_cache_bypass $http_authorization $cookie_session;
proxy_no_cache $http_authorization $cookie_session;
add_header X-Cache-Status $upstream_cache_status;
}
# 不缓存的路径
location /api/auth/ {
proxy_pass http://backend;
proxy_cache off; # 认证相关不缓存
}
}
}
缓存控制详解
location / {
proxy_pass http://backend;
proxy_cache my_cache;
# 基本缓存策略
proxy_cache_valid 200 10m;
proxy_cache_valid 301 302 1m;
proxy_cache_valid 404 30s;
# 缓存方法
proxy_cache_methods GET HEAD POST; # 默认只缓存 GET HEAD
# 缓存键
proxy_cache_key $scheme$host$uri$is_args$args;
# 包含 Cookie:proxy_cache_key $scheme$host$uri$is_args$args$cookie_user;
# 缓存条件:后端返回 Set-Cookie 时是否缓存
proxy_ignore_headers Set-Cookie Expires;
# ⚠️ 通常需要配合 proxy_hide_header Set-Cookie 使用
# 最小缓存请求次数
proxy_cache_min_uses 2; # 至少请求 2 次才缓存(防冷数据)
# 后端不可用时使用过期缓存
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
}
Microcaching(微缓存)
对动态内容做极短时间的缓存,大幅提升吞吐量:
proxy_cache_path /var/cache/nginx/micro
levels=1:2
keys_zone=micro:5m
max_size=1g;
server {
location / {
proxy_pass http://backend;
proxy_cache micro;
proxy_cache_valid 200 1s; # 只缓存 1 秒!
proxy_cache_lock on; # 缓存击穿保护
proxy_cache_lock_timeout 5s;
# 忽略后端的 Cache-Control 头
proxy_ignore_headers Cache-Control;
add_header X-Cache-Status $upstream_cache_status;
}
}
效果对比(假设后端响应时间 100ms):
无缓存:
10,000 req/s → 全部打到后端 → 后端扛不住
Microcache (1s):
10,000 req/s → 第 1 个请求打到后端 → 9999 个请求从缓存返回
↑ 后端只处理 1 req/s!1 秒后缓存刷新
适用场景:
- 高流量 API(如商品列表、新闻列表)
- 对数据实时性要求不苛刻(1 秒延迟可接受)
- 后端响应慢但不常变化的页面
缓存清除 / Cache Purge
# 方式 1:手动删除缓存文件
find /var/cache/nginx -type f -delete
nginx -s reload
# 方式 2:使用 ngx_cache_purge 模块(需要编译安装)
location ~ /purge(/.*) {
allow 127.0.0.1;
deny all;
proxy_cache_purge main_cache $scheme$host$1;
}
# 使用:curl -X PURGE http://example.com/purge/api/products
🔴 高级 / Advanced — 缓存架构设计
多级缓存架构
Client 浏览器缓存
│ MISS
▼
CDN 缓存(Cloudflare / CloudFront)
│ MISS
▼
Nginx 缓存(proxy_cache)
│ MISS
▼
应用层缓存(Redis / Memcached)
│ MISS
▼
数据库
# Nginx 缓存层配置
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=app:100m
max_size=50g
inactive=7d;
server {
location /api/ {
proxy_pass http://backend;
proxy_cache app;
proxy_cache_valid 200 1h;
# 添加缓存状态头(方便 CDN 识别)
add_header X-Cache-Status $upstream_cache_status;
add_header Cache-Control "public, max-age=3600"; # CDN 也会缓存
# 条件请求支持(304 Not Modified)
proxy_cache_revalidate on;
proxy_set_header If-Modified-Since $http_if_modified_since;
proxy_set_header If-None-Match $http_if_none_match;
}
}
缓存预热 / Cache Warming
#!/bin/bash
# cache-warm.sh — 预热热门页面
URLS=(
"https://example.com/"
"https://example.com/products"
"https://example.com/api/products?page=1"
"https://example.com/api/categories"
)
for url in "${URLS[@]}"; do
curl -s -o /dev/null -w "%{http_code} $url\n" "$url"
done
# 配合 cron 定时执行
# 0 6 * * * /opt/scripts/cache-warm.sh
缓存与 Vary 头
# 根据 Accept-Encoding 分别缓存(gzip 和非 gzip)
# Nginx 默认会处理 Vary 头
# 根据语言缓存不同版本
proxy_cache_key $scheme$host$uri$is_args$args$http_accept_language;
# ⚠️ 注意:Vary 头会让缓存碎片化
# Vary: Accept-Language → 每种语言一个缓存副本
# Vary: User-Agent → 每种浏览器一个缓存副本(灾难性碎片化!)
# 清除危险的 Vary 头
proxy_hide_header Vary;
# 或者只保留必要的
proxy_ignore_headers Vary;
错误页面缓存与降级
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=app:100m
max_size=10g;
server {
location / {
proxy_pass http://backend;
proxy_cache app;
# 关键:后端故障时使用过期缓存
proxy_cache_use_stale
error # 连接错误
timeout # 读取超时
updating # 正在更新缓存时
http_500 # 500 错误
http_502 # 502 错误
http_503 # 503 错误
http_504; # 504 错误
# 后台更新缓存(用户不等待)
proxy_cache_background_update on;
# 防缓存击穿
proxy_cache_lock on;
proxy_cache_lock_age 5s;
proxy_cache_lock_timeout 5s;
}
}
缓存监控
# 查看缓存目录大小
du -sh /var/cache/nginx/
# 查看缓存文件数量
find /var/cache/nginx -type f | wc -l
# Nginx 内置缓存状态(需启用 stub_status)
# 见下一章:日志与监控
小结 / Summary
| 层级 | 你需要知道的 / What You Need to Know |
|---|---|
| 🟢 基础 | proxy_cache_path + proxy_cache,proxy_cache_valid |
| 🟡 进阶 | Microcaching,缓存条件控制,proxy_cache_bypass,缓存清除 |
| 🔴 高级 | 多级缓存架构,缓存预热,Vary 头管理,错误降级,防缓存击穿 |