在Nginx中开启sendfile几乎是所有性能优化文章都会提到的一步,但很多人只是照抄了sendfile on;这一行配置,并不清楚它到底做了什么、什么时候该开、什么时候不该开。这篇文章就把静态资源传输这条链路彻底讲透,从传统方式的瓶颈讲到零拷贝原理,再给出可以直接落地的完整配置。

传统文件传输的瓶颈在哪里
先看没有sendfile时,Nginx如何把一个文件发送给客户端。当请求到达后,Nginx的worker进程会调用操作系统的read()函数,把文件数据从磁盘读到内核缓冲区,再从内核缓冲区拷贝到用户态的应用缓冲区。这一步完成后,进程接着调用write()函数,数据又要从用户态缓冲区拷贝回内核的socket缓冲区,最后才由网卡发送出去。
整个过程一共发生了四次数据拷贝和四次上下文切换,而Nginx本身要做的仅仅是把这个文件原封不动地转发出去,它根本不需要在用户态查看或修改文件内容。也就是说,中间两次经过用户态的拷贝完全是浪费:既消耗了CPU,又占用了宝贵的用户态内存,还增加了系统调用的开销。
当并发量大、文件体积较大时,这种浪费会被成倍放大。服务器明明带宽还有富余,CPU使用率却居高不下,响应时间也开始抖动,很多性能问题的根源就在这里。理解了这个瓶颈,才能真正明白sendfile的价值。
sendfile的零拷贝原理与配置方法
sendfile是Linux内核从2.4版本开始提供的一个系统调用,它的设计目标很直接:如果只是把文件内容从磁盘送到网络,就不要让数据绕道用户态。开启之后,内核收到sendfile指令,会通过DMA方式把文件数据读入内核缓冲区,然后直接把文件描述符和偏移信息塞进socket缓冲区,网卡再通过scatter/gather机制直接从内核缓冲区取数据发送,整个过程只有两次拷贝,上下文切换也减少到两次,完全绕开了用户态。
在Nginx中开启它非常简单,指令可以放在http、server或location任意一层:
http {
# 在http层级全局开启
sendfile on;
server {
listen 80;
server_name ipipp.com;
location /static/ {
root /var/www/html;
sendfile on; # 也可以在location层级单独控制
}
}
}
需要注意默认值是off,也就是Nginx默认并没有使用这项优化,必须显式开启。配置修改后记得用nginx -t检查语法,再执行nginx -s reload平滑重载。
开启后的收益主要体现在两方面:一是CPU占用明显下降,因为少了一次内核态与用户态之间的来回拷贝,worker进程本身的工作量减轻了;二是高并发场景下吞吐量提升,静态文件越大、请求越密集,效果越明显。实测在一个大量分发图片和视频文件的服务器上,开启sendfile后CPU使用率能下降百分之二十到三十。
配合tcp_nopush与tcp_nodelay发挥最大效果
sendfile很少单独使用,它有一对黄金搭档:tcp_nopush和tcp_nodelay。这两个指令乍看矛盾,一个鼓励攒数据再发,一个鼓励有数据就立刻发,实际上它们配合sendfile后工作得非常好。
当tcp_nopush on;时,Nginx会尽量填满一个完整的TCP报文段再发送,同时把响应头一并打包在第一个数据包里。这对传输大文件很有利,减少了网络中小包的数量,降低了协议开销。而tcp_nodelay on;则是针对禁用了Nagle算法的情况,确保小数据包不被延迟发送,这对keepalive连接上的小响应很关键。
http {
sendfile on;
tcp_nopush on; # 配合sendfile,攒满一个报文段再发
tcp_nodelay on; # keepalive连接上小数据不延迟
# 静态资源站点完整示例
server {
listen 80;
server_name static.ipipp.com;
root /data/www/static;
location / {
# 常见静态文件类型的本地缓存时间
expires 7d;
add_header Cache-Control "public";
}
# 大文件目录单独优化
location /download/ {
sendfile on;
sendfile_max_chunk 512k; # 单次调用传输上限,避免长时间占用
tcp_nopush on;
}
}
}
上面配置中的sendfile_max_chunk也值得一提。它限制每次sendfile调用的数据量,默认是0表示不限制。在极端情况下,一次sendfile调用可能传输完整个大文件,期间worker进程会被长时间占用,其他请求排队。设置成512k可以让传输分片进行,worker在片段之间有机会处理其他事件,这对公平性有帮助。
使用中的注意事项与不适用的场景
sendfile并不是万能开关,有些场景下开启反而出问题。第一个典型场景是反向代理。当Nginx作为代理把上游返回的内容转发给客户端时,数据本身已经在用户态缓冲区里了,sendfile根本没有用武之地,此时配置不产生任何效果,也不会报错,只是被忽略。
第二个场景是开启了压缩的情况。如果location中同时配置了gzip on;,而响应内容需要压缩,Nginx必须先在用户态读取并压缩数据,这时sendfile会被自动禁用。也就是说,gzip和sendfile本质上互相排斥,Nginx内部会智能处理,但你要清楚此时真正起作用的是压缩而不是零拷贝。
第三点是某些老版本内核或特殊文件系统(比如部分网络挂载的NFS文件系统)上sendfile存在兼容性问题,历史上也出现过开启后传输损坏或断连的bug。生产环境升级前建议先在测试环境验证,观察error日志中是否有相关报错。
总结一下实践建议:纯静态文件服务(图片、CSS、JS、音视频、安装包)放心开启sendfile并搭配tcp_nopush;需要gzip压缩的内容不要指望sendfile提速;反向代理流量不受它影响。再配合expires缓存头、合理的open_file_cache设置,一套组合拳打下来,静态资源的响应速度和服务器承载能力都会有实打实的提升。
Nginx静态资源优化sendfile配置Nginx性能调优修改时间:2026-09-11 07:42:29