强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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_cacheproxy_cache_valid
🟡 进阶Microcaching,缓存条件控制,proxy_cache_bypass,缓存清除
🔴 高级多级缓存架构,缓存预热,Vary 头管理,错误降级,防缓存击穿

下一章:日志与监控 / Logging & Monitoring