nginx 的 sendfile 技术通过内核零拷贝显著提升大文件分发效率,需同时满足 location 显式启用、服务真实磁盘文件、响应含 content-length 三条件,并配合 tcp_nopush、禁用 gzip 等用户态处理方可生效。

Nginx 中 sendfile 技术能显著提升大文件分发效率,核心在于绕过用户态、由内核直接完成磁盘到网络的数据搬运,减少 CPU 拷贝和上下文切换。但它不是“一开就快”,必须满足运行时条件并配合其他指令才能真正生效。
sendfile 的本质是零拷贝路径
传统 read/write 方式需经历 4 次数据拷贝(磁盘→内核缓冲区→用户空间→socket 缓冲区→网卡),而 sendfile 启用后仅需 2 次(磁盘→内核缓冲区→网卡),Linux 2.4+ 还支持 SG-DMA,进一步省去内核缓冲区到 socket 的拷贝。这对 MP4、ISO、ZIP 等百 MB 以上文件的吞吐提升尤为明显——CPU 占用下降 30%~50%,QPS 上升,首屏响应更快。
必须同时满足三个硬性前提才走零拷贝
- 当前 location 显式配置
sendfile on:不能只靠 http 块继承,需在具体资源路径中写明; - 服务的是真实磁盘静态文件:例如
root /data/assets+location ~ \.mp4$,且文件存在、可读,未被 SELinux 或容器存储驱动拦截; - 响应头含
Content-Length:Nginx 需提前知道大小才能调用sendfile();若后端返回 chunked 编码、流式响应或无长度头,会自动回退到 read/write 模式。
tcp_nopush 必须与 sendfile 成对启用tcp_nopush on 的作用是让内核把 HTTP 响应头和文件开头数据合并进同一个 TCP 报文发出——但这只在 sendfile 路径下才可能实现。普通 read/write 模式中,数据分段进入发送队列,tcp_nopush 实际被忽略(日志提示 “tcp_nopush is ignored”)。因此:
- 必须搭配
tcp_nodelay off(默认即为 off),否则立即发包会抵消“攒包”效果; - 禁用
gzip on、proxy_buffering on、sub_filter等任何用户态处理,否则强制退出零拷贝路径; - 若用
gzip_static on提前提供.gz文件,仍可走 sendfile,这是推荐替代方案。
按文件类型精细化配置在 location 块
不要在 http 或 server 块全局开启,避免干扰 API、WebSocket 等低延迟服务。典型配置如下:
-
location ~ \.(mp4|webm|avi|zip|iso|tar\.gz|dmg)$ { -
root /data/assets; -
sendfile on; -
tcp_nopush on; -
tcp_nodelay off; -
gzip off; -
expires 7d; -
add_header Cache-Control "public, immutable";
若代理对象存储等后端,需加 proxy_buffering off 并确保上游返回 Content-Length;大文件建议设 sendfile_max_chunk 512k,防止单次传输阻塞事件循环。
验证是否真正生效
- 用
strace -p $(pgrep nginx) -e trace=sendfile64观察 worker 进程是否持续调用sendfile64系统调用; - 对比开启/关闭 sendfile 时的 CPU 使用率(
pidstat -u 1)和压测 QPS(如 wrk 测试 10MB 文件); - 检查 Nginx 错误日志是否有
sendfile() failed类报错,尤其在 NFS 或某些容器环境中。
不复杂但容易忽略











