Chapter 08

缓存与性能

缓存是最划算的性能优化——把后端算过一次的响应存起来复用,同时调好 worker 与连接参数,让 Nginx 火力全开。

8.1 核心概念

proxy_cache
Nginx 把后端响应缓存到本地磁盘,后续相同请求直接由 Nginx 返回,不再打后端。适合缓存可复用的动态响应。
缓存键(Cache Key)
决定"什么样的请求算同一个缓存"的字符串,默认由 scheme + 方法 + host + URI 组成。设计不当会导致命中率低或错误复用。
缓存区(keys_zone)
一块共享内存,存缓存条目的键和元数据;实际内容存磁盘。keys_zone=name:size 定义名称和内存大小。
FastCGI 缓存
针对 PHP-FPM 等 FastCGI 后端的缓存(fastcgi_cache),原理同 proxy_cache,常用于 WordPress 提速。
缓存状态 $upstream_cache_status
变量,值为 HIT/MISS/EXPIRED/BYPASS 等,写进响应头或日志用于观察命中率。
worker 调优
调整 worker_processes、worker_connections、文件描述符上限、sendfile 等参数,让 Nginx 充分利用硬件。

8.2 配置代理缓存

http {
    # 定义缓存路径 + 内存区(放 http 层)
    proxy_cache_path  /var/cache/nginx
                      levels=1:2
                      keys_zone=my_cache:10m
                      max_size=1g
                      inactive=60m
                      use_temp_path=off;

    server {
        location / {
            proxy_pass  http://backend;
            proxy_cache  my_cache;
            proxy_cache_valid  200 302  10m;   # 200/302 缓存 10 分钟
            proxy_cache_valid  404      1m;    # 404 缓存 1 分钟

            # 把命中状态暴露到响应头,方便调试
            add_header  X-Cache-Status  $upstream_cache_status;
        }
    }
}

8.3 缓存键与绕过

location / {
    proxy_pass  http://backend;
    proxy_cache  my_cache;

    # 自定义缓存键(默认已含 host+uri)
    proxy_cache_key  "$scheme$request_method$host$request_uri";

    # 登录用户/带 Cookie 时跳过缓存,直接回源
    proxy_cache_bypass  $cookie_session;
    proxy_no_cache      $cookie_session;

    # 后端抖动时用过期缓存兜底(可用性优先)
    proxy_cache_use_stale  error timeout updating http_500 http_502 http_503;
    proxy_cache_lock  on;   # 相同 key 只放一个请求回源,防击穿
}
别缓存了带用户态的响应

缓存最大的坑是把"张三的个人页"缓存后返回给了李四。凡是随用户/Cookie/Authorization 变化的响应,都要用 proxy_no_cache / proxy_cache_bypass 排除,或把用户标识纳入缓存键。只缓存真正公共的内容。

8.4 FastCGI 缓存(PHP)

http {
    fastcgi_cache_path  /var/cache/fcgi  levels=1:2
                        keys_zone=php_cache:10m  inactive=60m;

    server {
        location ~ \.php$ {
            include  fastcgi_params;
            fastcgi_pass  unix:/run/php/php-fpm.sock;
            fastcgi_cache  php_cache;
            fastcgi_cache_valid  200  10m;
            add_header  X-FCGI-Cache  $upstream_cache_status;
        }
    }
}

8.5 worker 与连接调优

# main context
worker_processes  auto;              # = CPU 核心数
worker_rlimit_nofile  100000;        # 每 worker 可打开的 fd 上限

events {
    worker_connections  16384;       # 每 worker 并发连接
    multi_accept  on;                # 一次事件尽量多接连接
    use  epoll;                      # Linux 事件模型(通常自动选)
}

http {
    sendfile  on;
    tcp_nopush  on;
    tcp_nodelay  on;
    keepalive_timeout  65;
    keepalive_requests  1000;        # 单连接可复用的请求数
    reset_timedout_connection  on;
}
先测量再调参

盲目把参数调到天上不会更快,反而可能耗尽内存。用 abwrkk6 压测,观察 CPU、内存、ss -s 连接数和错误日志,找到真正的瓶颈(往往是后端应用或数据库,而非 Nginx 本身)再针对性调整。

8.6 观察缓存命中率

# 在日志格式里加入缓存状态
log_format  cachelog  '$remote_addr $status $upstream_cache_status '
                      '"$request" rt=$request_time';

# 统计各命中状态出现次数
$ awk '{print $3}' /var/log/nginx/access.log | sort | uniq -c
   8421 HIT
    932 MISS
    115 EXPIRED
缓存分层

完整的性能栈通常是多层缓存:浏览器缓存(Cache-Control)→ CDN → Nginx proxy_cache → 应用缓存(Redis)→ 数据库。每往前一层拦住请求,成本越低。Nginx 缓存是"离用户很近又几乎零成本"的一层,性价比极高。

8.7 小结与下一步