启用 open_file_cache 可压平高并发下日志写入引发的 CPU 小毛刺和 I/O 波动,它仅复用已打开的文件描述符和元信息,跳过每次 open/close 系统调用。

直接用 open_file_cache 缓存日志文件句柄,能有效压平高并发下因频繁 open/close 日志引发的 CPU 小毛刺和 I/O 波动。它不缓存日志内容,只复用已打开的 fd 和元信息,让每条日志写入跳过系统级文件打开流程。
为什么日志会产生“小毛刺”
默认情况下,Nginx 每次写 access_log 都会触发一次 open() + write() + close()(尤其在未启用缓冲或轮转频繁时)。当 QPS 上千、日志路径又按时间/主机名动态生成时,大量进程争抢文件句柄、内核 inode 查找、磁盘 seek,就会在 top 或 perf 中看到周期性 CPU 尖峰和 iostat 中的 await 抖动——这就是“小毛刺”的根源。
关键配置:四参数必须齐备
仅写 open_file_cache on 无效。需在 http 块中同步配置:
- open_file_cache max=2000 inactive=90s:设为 2000 起步,覆盖多虚拟主机+按日期切分的日志路径;90s 淘汰窗口比默认 60s 更适配日志写入节奏
- open_file_cache_valid 30s:每 30 秒校验一次日志文件是否仍可写(比如 logrotate 后原文件被 mv,新文件未重建)
- open_file_cache_min_uses 3:同一日志路径需在 90s 内被写入至少 3 次才进缓存,防止单次调试日志或错误路径占位
- open_file_cache_errors on:缓存“Permission denied”或“No such file”等错误状态,避免反复探测缺失日志目录
配合策略才能稳住句柄数
缓存本身不持有 fd,真正减少句柄波动要靠协同控制:
- 关闭非必要日志:
location ~* \.(js|css|png|woff2)$ { access_log off; },从源头砍掉 60%+ 的日志写入量 - 启用缓冲写入:
access_log /var/log/nginx/app.log main buffer=128k flush=3s;,批量落盘降低系统调用密度 - 轮转改按大小而非时间:
logrotate配置size=100M,避免每分钟强制 close 导致缓存频繁失效 - 调大系统限制:
worker_rlimit_nofile 65535;,防止缓存条目增多后触发进程级句柄上限
验证有没有真起效
别只看 reload 成功,盯住三个实时指标:
- 执行
lsof -p $(pgrep nginx) | grep 'REG.*log' | wc -l:开启前随请求线性增长,开启后稳定在max值附近(如 1980±20) - 查 error_log 是否有
open_log_file_cache: cache miss:持续出现说明min_uses过高或轮转太勤;出现cache full则需调大max - 压测时运行
iostat -x 1:若%util峰值下降 30%+、await抖动消失,说明磁盘 I/O 毛刺已被抑制

















