sendfile是nginx大文件下载场景下实现零拷贝、压降cpu、打满带宽的核心机制,需在location块显式启用并满足真实静态文件、content-length头、禁用gzip/etag等条件,配合tcp_nopush on与tcp_nodelay off方可生效。

在大流量下载场景下,sendfile 是 Nginx 提升吞吐、压降 CPU 的核心机制,不是“可选优化”,而是决定带宽能否打满的关键开关。它让单台服务器在高并发下载中稳定服务数万连接,同时避免 CPU 被数据搬运拖垮。
为什么大文件下载特别依赖 sendfile
当用户批量下载 MP4、ISO、ZIP 等百 MB 以上文件时,传统 read+write 模式会迅速成为瓶颈:
- 每次传输都要把文件内容从磁盘拷进用户态内存,再拷进 socket 缓冲区——4 次数据搬移 + 4 次上下文切换
- CPU 大量时间花在 memcpy 上,而非调度或协议处理
- 内存带宽和缓存压力陡增,尤其在多并发小块读时更明显
启用 sendfile 后,路径压缩为:磁盘 → 内核页缓存 → socket 缓冲区 → 网卡(DMA 直传)。全程零用户态拷贝,CPU 开销下降 40%–60%,实测千兆网卡带宽利用率可从 60% 提升至 95%+。
必须满足的三个硬性条件才能生效
配置了 sendfile on 不等于真正启用。Nginx 在每次响应时动态判断,缺一即退化为低效模式:
-
location 级显式开启:不能只在 http 块写,必须在服务下载路径的 location 中重复声明,例如:
location /dl/ { sendfile on; ... } - 命中真实本地静态文件:root 或 alias 必须指向本地磁盘路径;proxy_pass、FastCGI、Lua 处理等均不触发 sendfile
- 响应头含明确 Content-Length:Nginx 需预知文件大小才能调用 sendfile();若后端流式返回、chunked 编码或缺失该头,自动 fallback
搭配 tcp_nopush 才能发挥最大效能
sendfile 生成的是连续数据流,但若不加控制,内核可能拆成大量小 TCP 包(如每包仅几百字节),浪费网络开销。tcp_nopush 的作用就是“攒包”:
- 配合 sendfile 使用时,等数据填满 MSS(通常 1448 字节)再发,显著提升单包载荷率
- 必须同步关闭 tcp_nodelay(设为
off),否则两者冲突,nopush 被忽略 - 推荐组合:
sendfile on; tcp_nopush on; tcp_nodelay off;,且仅用于明确的大文件 location
常见失效原因与验证方法
即使配置正确,以下情况也会导致 sendfile 自动禁用:
- 启用了
gzip on(运行时压缩强制进入用户态) - 使用了
etag、expires或其他需动态计算响应头的指令 - 客户端发起 Range 请求(如视频拖拽),Nginx 需切片处理
- 文件存储在 NFS/CIFS 等不支持 sendfile 的远程文件系统上
验证是否真实生效,最直接方式是运行:strace -e trace=sendfile -p $(pgrep nginx)
观察 worker 进程是否持续调用 sendfile 系统调用。若大量出现 read/write,则已退化。











