sendfile能显著提升静态文件传输速率,核心是将传统四次数据拷贝(磁盘→内核→用户态→socket→网卡)压缩为两次(磁盘→内核→socket→网卡),降低CPU开销;需配套sendfile on、tcp_nopush on、tcp_nodelay off三者协同,避开gzip、sub_filter、非本地文件等失效条件,并通过strace或压测验证实际生效。

sendfile 能显著提升静态文件的传输速率,核心在于它把原本需要用户态参与的四次数据拷贝压缩为两次,大幅降低 CPU 开销和延迟。实际压测中,大文件(如 1MB+ 图片或 JS)QPS 可提升 20%–50%,CPU 的系统态(sy)占比明显下降。
为什么能提速:从四次拷贝到两次
传统 read/write 方式下,Nginx 必须把文件从磁盘读进用户空间缓冲区,再写入 socket 缓冲区,过程如下:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → 用户空间缓冲区(CPU 拷贝)
- 用户空间缓冲区 → socket 缓冲区(CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
启用 sendfile 后,内核直接在内部完成搬运:
- 磁盘 → 内核缓冲区(DMA)
- 内核缓冲区 → socket 缓冲区(内核态直传,无 CPU 拷贝)
- socket 缓冲区 → 网卡(DMA)
Linux 2.4+ 还支持 SG-DMA,可进一步跳过中间缓冲区,接近真正“零拷贝”。
必须配套的三个关键配置
单独写 sendfile on; 不够,需三者协同生效:
- sendfile on; —— 启用内核直送能力
- tcp_nopush on; —— 让响应头和文件数据合并发送,减少小包数量
- tcp_nodelay off; —— 关闭 Nagle 算法,避免与 tcp_nopush 冲突
这三项建议统一写在 http 块顶层,确保所有静态 location 继承生效。
哪些情况会让提速失效
即使配置正确,以下任一情形都会导致 sendfile 自动退化为 read/write:
- 启用了 gzip on 或 gunzip on(压缩必须走用户态)
- 使用了 sub_filter、add_before_body 等内容改写指令
- 文件不在本地 ext4/XFS 文件系统上(如 NFSv3、tmpfs、Docker overlay2 默认驱动)
- 请求带 Range 头(如视频拖拽),Nginx 需切片处理
- 通过 proxy_pass、fastcgi_pass 等代理返回的内容(非真实磁盘文件)
怎么确认真的提速了
不能只看配置是否写对,要验证运行时是否调用 sendfile:
- 用 strace -p $(pgrep nginx | head -1) -e trace=sendfile64 观察 worker 进程是否持续触发该系统调用
- 对比开启前后,用 ab 或 wrk 压测同一静态文件,关注 QPS 和 CPU sy 占比变化
- 检查 Nginx 错误日志是否有 sendfile() failed 类报错,提示底层不支持


















