基本概念
Stale-While-Revalidate (SWR) 是一种缓存策略,允许在后台更新缓存的同时,立即返回已缓存的(可能过期的)内容给用户。这种策略在性能和数据新鲜度之间取得了平衡。
核心思想
- 立即响应:当缓存内容过期后,仍然先返回缓存的"陈旧"数据
- 后台更新:同时在后台发起请求获取最新数据
- 下次使用:更新后的数据会在下一次请求时使用
工作流程
dot请求 → 检查缓存 → 缓存已过期? ↓ 是:返回陈旧数据 + 后台更新 ↓ 否:直接返回缓存数据
HTTP Cache-Control 中的使用
语法
Cache-Control: max-age=<seconds>, stale-while-revalidate=<seconds>
参数说明
- max-age: 资源被认为是新鲜的时间(秒)
- stale-while-revalidate: 资源过期后,仍可使用陈旧缓存的时间窗口(秒)
示例 1:基本 HTTP 响应头
httpHTTP/1.1 200 OK Content-Type: application/json Cache-Control: max-age=60, stale-while-revalidate=300 {"data": "some content"}
解释:
- 前 60 秒:缓存是新鲜的,直接使用
- 60-360 秒:缓存过期,但返回陈旧数据并后台更新
- 360 秒后:必须等待新数据
示例 2:Nginx 基础配置
nginxserver { listen 80; server_name api.example.com; location /api/data { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=3600, stale-while-revalidate=86400"; } }
场景:API 数据缓存 1 小时,过期后 24 小时内仍可使用陈旧数据。
示例 3:不同内容类型的配置
nginxserver { listen 80; server_name api.example.com; # 动态内容 - 短缓存 location /api/news { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=60, s-maxage=300, stale-while-revalidate=1800"; } # 静态资源 - 长缓存 location /static/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=86400, s-maxage=604800, stale-while-revalidate=2592000"; } # 个性化内容 - 不使用 CDN 缓存 location /api/user/ { proxy_pass http://backend:3000; add_header Cache-Control "private, max-age=300, stale-while-revalidate=1800"; } }
示例 4:结合 Nginx 缓存
nginx# 定义缓存路径 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m; server { listen 80; server_name api.example.com; location /api/ { proxy_pass http://backend:3000; proxy_cache my_cache; proxy_cache_valid 200 5m; # Nginx 自身使用 SWR 策略 proxy_cache_use_stale updating error timeout; proxy_cache_background_update on; # 设置下游(CDN + 浏览器)的缓存策略 add_header Cache-Control "public, max-age=60, s-maxage=300, stale-while-revalidate=1800"; add_header X-Cache-Status $upstream_cache_status; } }
说明:
proxy_cache_use_stale updating:Nginx 更新缓存时返回陈旧数据proxy_cache_background_update on:后台更新缓存add_header Cache-Control:设置 CDN 和浏览器的缓存行为
CDN 配置说明
CDN 自动识别
大多数 CDN(Cloudflare、AWS CloudFront、Fastly、阿里云 CDN 等)会自动识别源站返回的 Cache-Control 头,无需额外配置。
工作原理:
- 源站 Nginx 设置响应头:
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=1800 - CDN 自动读取
s-maxage=300,缓存 5 分钟 - CDN 自动读取
stale-while-revalidate=1800,在 5-30 分钟内使用陈旧数据 - 浏览器自动读取
max-age=60,缓存 1 分钟
源站配置示例
nginxserver { listen 80; server_name api.example.com; location /api/ { proxy_pass http://backend:3000; # 设置响应头,CDN 和浏览器自动处理 add_header Cache-Control "public, max-age=60, s-maxage=300, stale-while-revalidate=1800"; } }
效果:
- 浏览器:1 分钟新鲜,30 分钟陈旧期
- CDN:5 分钟新鲜,30 分钟陈旧期
- 源站:最多每 5 分钟回源一次
优势与适用场景
优势
- 改善用户体验:减少等待时间,即使数据略微过期
- 降低服务器负载:减少同步请求压力
- 提高可用性:后端暂时不可用时仍能提供服务
- 平滑更新:用户不会感知到数据更新过程
适用场景
- 社交媒体动态:略微过期的内容可以接受
- 产品列表:价格和库存变化不是实时关键
- 用户资料:个人信息更新频率低
- 统计数据:仪表板数据可以有短暂延迟
- 静态资源:CSS、JS 文件的版本更新
不适用场景
- 金融交易:需要实时准确数据
- 库存扣减:必须保证数据一致性
- 安全认证:不能使用过期的认证信息
- 实时竞价:数据新鲜度至关重要
注意事项
- 数据一致性:确保陈旧数据不会导致业务逻辑错误
- 时间设置:合理设置 max-age 和 stale-while-revalidate 的值
- 错误处理:后台更新失败时的降级策略
- 缓存键设计:避免缓存键冲突
- 监控指标:跟踪缓存命中率和陈旧数据使用率
与其他策略的对比
| 策略 | 响应速度 | 数据新鲜度 | 服务器负载 |
|---|---|---|---|
| No Cache | 慢 | 最新 | 高 |
| Cache-First | 快 | 可能过期 | 低 |
| Network-First | 慢 | 最新 | 高 |
| Stale-While-Revalidate | 快 | 较新 | 中 |
SWR 在网关层的配置应用
核心理解
SWR 的配置建议在网关层(Nginx、Apache 等)完成,通过设置 HTTP 响应头来控制浏览器和 CDN 的缓存行为。
用户浏览器 → CDN → 源站网关(Nginx) → 应用服务器
↓ ↓ ↓
浏览器缓存 CDN缓存 设置缓存头
关键点:
- 源站的 Nginx 设置
Cache-Control响应头 - CDN 和浏览器根据这些响应头自动处理缓存
- 应用代码无需关心缓存逻辑
Nginx 配置示例
基础配置
nginx# 定义缓存路径 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m; server { listen 80; server_name api.example.com; # 动态 API location /api/ { proxy_pass http://backend:3000; proxy_cache api_cache; proxy_cache_valid 200 5m; # Nginx 自身的 SWR 行为 proxy_cache_use_stale updating error timeout; proxy_cache_background_update on; # 设置下游(CDN + 浏览器)的缓存头 add_header Cache-Control "public, max-age=60, s-maxage=300, stale-while-revalidate=1800"; add_header X-Cache-Status $upstream_cache_status; } # 静态资源 location /static/ { proxy_pass http://backend:3000; proxy_cache api_cache; proxy_cache_valid 200 1d; proxy_cache_use_stale updating error timeout; proxy_cache_background_update on; add_header Cache-Control "public, max-age=86400, s-maxage=604800, stale-while-revalidate=2592000"; } }
配置说明:
proxy_cache_use_stale updating:Nginx 更新缓存时返回陈旧数据proxy_cache_background_update on:后台更新缓存add_header Cache-Control:设置发送给 CDN 和浏览器的缓存策略
不同内容类型的配置
nginxserver { listen 80; server_name api.example.com; # 实时数据 - 最短缓存 location /api/realtime/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=5, s-maxage=10, stale-while-revalidate=30"; } # 动态内容 - 短缓存 location /api/dynamic/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=60, s-maxage=300, stale-while-revalidate=1800"; } # 半静态内容 - 中等缓存 location /api/content/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=600, s-maxage=1800, stale-while-revalidate=7200"; } # 静态资源 - 长缓存 location /static/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=86400, s-maxage=604800, stale-while-revalidate=2592000"; } # 个性化内容 - 不使用 CDN 缓存 location /api/user/ { proxy_pass http://backend:3000; add_header Cache-Control "private, max-age=60, stale-while-revalidate=300"; } }
多层缓存效果
以 /api/dynamic/ 为例:
Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=1800
浏览器行为:
├─ 0-60秒:使用缓存(新鲜)
├─ 60秒-30分钟:返回陈旧数据 + 后台请求 CDN
└─ 30分钟后:必须请求 CDN
CDN 行为:
├─ 0-5分钟:使用缓存(新鲜)
├─ 5-30分钟:返回陈旧数据 + 后台请求源站
└─ 30分钟后:必须请求源站
源站压力:
- CDN 最多每 5 分钟回源一次
- 大部分请求被 CDN 和浏览器拦截
CDN 配置
Cloudflare
Cloudflare 会自动识别源站的 Cache-Control 头,无需额外配置。
源站 Nginx 配置:
nginxlocation /api/ { proxy_pass http://backend:3000; add_header Cache-Control "public, max-age=60, s-maxage=600, stale-while-revalidate=3600"; }
Cloudflare 会自动:
- 使用
s-maxage=600作为 CDN 缓存时间 - 在 600-3600 秒内使用 SWR 策略
其他 CDN
大多数 CDN(如 AWS CloudFront、Fastly、阿里云 CDN)都会自动识别 s-maxage 和 stale-while-revalidate。
配置建议表
| 内容类型 | max-age | s-maxage | stale-while-revalidate | 说明 |
|---|---|---|---|---|
| 实时数据 | 5-10s | 10-30s | 30-60s | 股票、比分 |
| 动态内容 | 1-5min | 5-30min | 30-60min | 新闻、动态 |
| 半静态内容 | 10-30min | 1-6h | 6-24h | 产品列表 |
| 静态资源 | 1-7d | 7-30d | 30-90d | 图片、CSS |
| 不可变资源 | 1y | 1y | - | 带哈希的文件 |
| 个性化内容 | 1-5min | 不设置 | 5-30min | 用户数据 |
监控和调试
查看缓存状态
bash# 查看响应头 curl -I https://api.example.com/api/data # 输出示例: # Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=1800 # Age: 120 # X-Cache: HIT # X-Cache-Status: HIT
Age 头解释:
Age: 120表示缓存已存在 120 秒- 如果 Age < s-maxage,缓存是新鲜的
- 如果 s-maxage < Age < s-maxage + swr,使用陈旧数据
Nginx 日志配置
nginxlog_format cache_log '$remote_addr - $upstream_cache_status [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"'; access_log /var/log/nginx/cache.log cache_log;
注意事项
- 个性化内容不要用 s-maxage:避免 CDN 缓存用户私有数据
- 合理设置时间:stale-while-revalidate 应该比 max-age/s-maxage 长
- 监控回源率:通过日志分析缓存效果
- 测试验证:配置后用 curl 验证响应头是否正确
总结
Stale-While-Revalidate 是一种实用的缓存策略,特别适合对数据新鲜度要求不是极其严格,但又希望提供快速响应的场景。
要点
- 在网关层配置:通过 Nginx 等网关设置
Cache-Control响应头 - 自动生效:CDN 和浏览器会自动识别并执行 SWR 策略
- s-maxage 组合:为 CDN 和浏览器设置不同的缓存时间
- 多层缓存:浏览器 + CDN 多层缓存可大幅降低源站压力
- 简单配置:无需在应用代码中实现复杂逻辑
配置原则
- 实时数据:短缓存(秒级)
- 动态内容:中等缓存(分钟级)
- 静态资源:长缓存(天级)
- 个性化内容:使用
private,不设置s-maxage