Skip to content

深入理解 Nginx 缓存机制与性能优化

本文是 Nginx 实操专栏的第六篇,我们将深入探讨 Nginx 的缓存机制、各种缓存策略以及性能优化技巧,帮助你构建更快速、更高效的 Web 应用。

引言

在现代 Web 应用中,性能优化是一个永恒的话题。用户期望页面能够快速加载,而缓存技术是提升性能的关键手段之一。Nginx 作为一个高性能的 Web 服务器和反向代理,提供了强大的缓存功能。

在本文中,我们将详细介绍 Nginx 的缓存机制,包括静态资源缓存、反向代理缓存,以及各种性能优化技巧。

缓存基础概念

什么是缓存?

缓存是一种临时存储机制,用于存储经常访问的数据副本,以便后续请求可以更快地获取这些数据,而无需重新计算或从原始源获取。

缓存的类型

  1. 浏览器缓存 - 客户端浏览器存储的资源副本
  2. 代理缓存 - 中间代理服务器存储的资源副本
  3. 应用缓存 - 应用服务器端的缓存
  4. CDN 缓存 - 内容分发网络中的缓存

缓存的优势

  1. 减少延迟 - 从缓存中获取数据比从原始源获取更快
  2. 降低带宽消耗 - 减少重复数据传输
  3. 减轻服务器负载 - 减少对后端服务器的请求
  4. 提高可用性 - 即使原始服务器不可用,缓存仍可提供服务

静态资源缓存

静态资源(如图片、CSS、JavaScript 文件)是缓存的主要对象。

基本缓存配置

nginx
server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;
    
    # 图片资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;
    }
    
    # CSS 和 JavaScript 资源缓存
    location ~* \.(css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;
    }
    
    # 字体文件缓存
    location ~* \.(woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public";
        add_header Vary Accept-Encoding;
    }
    
    # 其他静态资源
    location /static/ {
        expires 1M;
        add_header Cache-Control "public";
    }
}

缓存控制头详解

Expires 头

nginx
expires 1y;     # 1年
expires 30d;    # 30天
expires 12h;    # 12小时
expires 30m;    # 30分钟
expires 10s;    # 10秒
expires epoch;  # 立即过期
expires max;    # 最大过期时间
expires off;    # 禁用 expires

Cache-Control 头

nginx
# 公共缓存,可以被任何缓存存储
add_header Cache-Control "public";

# 私有缓存,只能被浏览器缓存
add_header Cache-Control "private";

# 不缓存
add_header Cache-Control "no-cache, no-store, must-revalidate";

# 不变缓存,表示资源不会改变
add_header Cache-Control "public, immutable";

# 缓存但需要验证
add_header Cache-Control "public, must-revalidate";

ETag 和 Last-Modified

Nginx 自动为静态资源生成 ETag 和 Last-Modified 头:

nginx
# 启用 ETag
etag on;

# 启用 Last-Modified
if_modified_since exact;

反向代理缓存

Nginx 可以缓存后端服务器的响应,减少对后端的请求。

基本代理缓存配置

nginx
# 定义缓存路径和参数
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_lock on;
        
        # 添加缓存状态头
        add_header X-Cache-Status $upstream_cache_status;
    }
}

缓存路径参数详解

nginx
proxy_cache_path /var/cache/nginx 
    levels=1:2                  # 目录层级结构
    keys_zone=my_cache:10m      # 共享内存区域名称和大小
    max_size=10g                # 缓存最大大小
    inactive=60m                # 60分钟未访问的缓存将被清除
    use_temp_path=off           # 不使用临时路径
    loader_sleep=50ms           # 加载器睡眠时间
    loader_files=1000;          # 每次加载的文件数

缓存有效性控制

nginx
# 根据响应状态码设置不同的缓存时间
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid any 1m;

# 根据请求方法设置缓存
proxy_cache_methods GET HEAD POST;

# 根据内容类型设置缓存
map $sent_http_content_type $no_cache_types {
    default 0;
    "text/html" 1;
    "application/json" 1;
}

proxy_cache_valid 200 1h;
proxy_cache_valid 404 1m;
proxy_no_cache $no_cache_types;

缓存键定制

nginx
# 自定义缓存键
proxy_cache_key "$scheme$request_method$host$request_uri";

# 包含更多变量的缓存键
proxy_cache_key "$scheme$request_method$host$request_uri$http_accept_language$http_accept_encoding";

# 对于 API 请求,可能需要忽略查询参数顺序
proxy_cache_key "$scheme$request_method$host$uri";

缓存状态监控

nginx
# 添加缓存状态头
add_header X-Cache-Status $upstream_cache_status;

# 更详细的缓存信息
add_header X-Cache-Key $request_uri;
add_header X-Cache-Host $upstream_addr;

高级缓存策略

分层缓存

nginx
# L1 缓存 - 内存缓存
proxy_cache_path /var/cache/nginx/l1 levels=1:2 keys_zone=l1_cache:100m max_size=1g inactive=10m;

# L2 缓存 - 磁盘缓存
proxy_cache_path /var/cache/nginx/l2 levels=1:2 keys_zone=l2_cache:500m max_size=10g inactive=1h;

upstream backend {
    server backend1.example.com;
    server backend2.example.com;
}

server {
    listen 80;
    server_name example.com;
    
    location /api/fast-changing/ {
        proxy_pass http://backend;
        proxy_cache l1_cache;
        proxy_cache_valid 200 1m;
        add_header X-Cache-Level L1;
    }
    
    location /api/slow-changing/ {
        proxy_pass http://backend;
        proxy_cache l2_cache;
        proxy_cache_valid 200 1h;
        add_header X-Cache-Level L2;
    }
}

条件缓存

nginx
# 根据用户角色决定是否缓存
map $http_authorization $no_cache {
    default 0;
    "~*admin" 1;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 1h;
        proxy_no_cache $no_cache;
        proxy_cache_bypass $no_cache;
    }
}

缓存预热

nginx
# 定期预热缓存
location /warm-cache {
    internal;
    proxy_pass http://backend;
    proxy_cache my_cache;
    proxy_cache_valid 200 1h;
}

Gzip 压缩优化

压缩可以显著减少传输数据量,提高页面加载速度。

基本 Gzip 配置

nginx
# 启用 gzip 压缩
gzip on;

# 启用对 HTTP/1.1 的 gzip 压缩
gzip_http_version 1.1;

# 启用对代理请求的 gzip 压缩
gzip_proxied any;

# 设置 Vary 头
gzip_vary on;

# 设置压缩级别(1-9,数字越大压缩比越高但消耗更多 CPU)
gzip_comp_level 6;

# 设置最小压缩文件大小
gzip_min_length 1024;

# 设置需要压缩的 MIME 类型
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml+rss
    application/atom+xml
    image/svg+xml;

预压缩静态文件

对于不经常变化的静态文件,可以预先压缩并使用 [gzip_static] 指令:

nginx
location ~* \.(css|js|html|xml)$ {
    gzip_static on;
    expires 1y;
    add_header Cache-Control "public, immutable";
}

预压缩文件需要手动创建或使用构建工具生成,文件名以 .gz 结尾:

style.css
style.css.gz

性能优化技巧

连接优化

nginx
# 启用 sendfile
sendfile on;

# 启用 tcp_nopush
tcp_nopush on;

# 启用 tcp_nodelay
tcp_nodelay on;

# 设置 keepalive 超时时间
keepalive_timeout 65;

# 设置客户端请求体缓冲区大小
client_body_buffer_size 128k;

# 设置客户端请求头缓冲区大小
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;

工作进程优化

nginx
# 设置工作进程数
worker_processes auto;

# 设置每个工作进程的最大连接数
events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

# 设置工作进程 CPU 亲和性
worker_cpu_affinity auto;

缓冲区优化

nginx
# 设置代理缓冲区
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;

# 设置 FastCGI 缓冲区
fastcgi_buffering on;
fastcgi_buffer_size 4k;
fastcgi_buffers 8 4k;
fastcgi_busy_buffers_size 8k;

缓存管理

缓存清除

虽然 Nginx Plus 提供了缓存清除 API,但开源版本需要通过其他方式实现:

nginx
# 使用第三方模块 nginx-cache-purge
location ~ /purge(/.*) {
    proxy_cache_purge my_cache $scheme$request_method$host$1;
}

或者通过脚本删除缓存文件:

bash
# 删除特定 URL 的缓存
find /var/cache/nginx -name "*$(echo -n 'http://example.com/some/path' | md5sum | cut -d' ' -f1)*" -delete

缓存监控

nginx
# 启用缓存状态页面
location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

# 自定义缓存监控
location /cache_status {
    access_log off;
    allow 127.0.0.1;
    deny all;
    
    # 返回缓存统计信息
    return 200 "Cache zone: my_cache\nCache size: $upstream_cache_status";
}

实际应用示例

综合优化配置

nginx
# 定义缓存路径
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:100m max_size=10g inactive=60m use_temp_path=off;

# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml+rss
    application/atom+xml
    image/svg+xml;

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;
    
    # 静态资源缓存
    location ~* \.(jpg|jpeg|png|gif|ico|svg|css|js|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept-Encoding;
        
        # 预压缩文件支持
        gzip_static on;
    }
    
    # API 缓存
    location /api/ {
        proxy_pass http://backend;
        proxy_cache my_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_lock on;
        
        # 添加缓存状态头
        add_header X-Cache-Status $upstream_cache_status;
        
        # 代理头设置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    # 动态内容不缓存
    location ~* \.(php|jsp|asp)$ {
        proxy_pass http://backend;
        proxy_cache_bypass 1;
        proxy_no_cache 1;
    }
}

最佳实践总结

1. 缓存策略选择

nginx
# 静态资源 - 长期缓存
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# HTML 页面 - 短期缓存或不缓存
location ~* \.html$ {
    expires 1h;
    add_header Cache-Control "public, must-revalidate";
}

# API 响应 - 根据内容类型设置缓存时间
location /api/ {
    proxy_pass http://backend;
    proxy_cache my_cache;
    proxy_cache_valid 200 10m;
    proxy_cache_valid 404 1m;
}

2. 性能优化配置

nginx
# 全局性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;

# 缓冲区优化
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;

# 工作进程优化
worker_processes auto;
worker_connections 1024;
worker_cpu_affinity auto;

3. 安全的缓存配置

nginx
# 避免缓存敏感内容
location ~* (login|admin|user) {
    proxy_pass http://backend;
    proxy_cache_bypass 1;
    proxy_no_cache 1;
}

# 根据认证状态决定是否缓存
map $http_authorization $no_cache {
    default 0;
    "~*" 1;
}

location / {
    proxy_pass http://backend;
    proxy_cache my_cache;
    proxy_no_cache $no_cache;
    proxy_cache_bypass $no_cache;
}

总结

通过合理配置 Nginx 的缓存机制和性能优化参数,我们可以显著提升 Web 应用的响应速度和用户体验。关键要点包括:

  1. 针对不同类型的内容设置合适的缓存策略
  2. 合理配置代理缓存以减少后端负载
  3. 启用 Gzip 压缩以减少传输数据量
  4. 优化连接和缓冲区参数以提高性能
  5. 定期监控和管理缓存以确保其有效性

在下一章中,我们将探讨 Nginx 与前端应用的集成,特别是如何支持 SPA(单页应用)和实现前端路由。

要点回顾:

  1. 静态资源缓存通过设置适当的 Expires 和 Cache-Control 头实现
  2. 反向代理缓存可以显著减少对后端服务器的请求
  3. Gzip 压缩和预压缩可以减少传输数据量
  4. 合理的连接和缓冲区配置可以提高性能
  5. 缓存管理和监控对于维护缓存系统的健康运行至关重要