sendfile 本身不造成写性能损耗,其表现取决于页缓存命中率与底层存储延迟;冷读受最慢设备(如hdd)制约,热读则趋同;需分层测试page cache+sendfile联动效果,避免因gzip、range等配置导致退化。

直接在异构存储(如混合使用 NVMe、SATA SSD 和 HDD)上测 sendfile 的“读写性能损耗”本身是个误区——sendfile 本身不参与写操作,也不做磁盘写入;它只负责把内核页缓存(Page Cache)里的数据零拷贝发往 socket。所谓“损耗”,实际反映的是底层存储响应延迟、缓存命中率、DMA 效率等综合影响,而非 sendfile 自身变慢。
明确测试目标:不是测 sendfile,而是测 Page Cache + sendfile 联动效果
sendfile 的性能表现高度依赖文件是否已在内核页缓存中。异构存储下关键变量是:
- 首次访问(冷读):数据需从磁盘加载进 Page Cache,延迟由最慢设备(如 HDD)决定
- 重复访问(热读):完全走内存路径,此时存储类型差异被屏蔽,sendfile 效能趋同
- 大文件连续读 vs 小文件随机读:前者利于预读和 DMA 批量搬运,后者易受 HDD 寻道延迟拖累
分层隔离测试法:拆解异构影响链
避免笼统压测,应按路径分段验证:
-
确认 Page Cache 命中状态:用
cat /proc/meminfo | grep -i "cached\|page"和fincore --only-cached检查目标文件是否已驻留缓存 -
绕过网络干扰,测纯内核路径延迟:用
dd if=/path/to/file of=/dev/null iflag=direct bs=128k对比不同盘的 raw I/O 吞吐,再用time cat file > /dev/null测带 cache 的访问延迟 -
实测 sendfile 实际调用路径:用
strace -p $(pgrep nginx -f 'worker') -e trace=sendfile,read,write观察高并发下是否持续触发sendfile系统调用;若大量混杂read,说明因 range、gzip、etag 等触发退化
针对性压测设计(推荐 wrk + 固定文件集)
准备四类典型文件(分别放于不同挂载点),统一用 sendfile on 配置服务:
- 1KB 日志片段(HDD 上)→ 模拟 tail 场景
- 1MB 静态资源(NVMe 上)→ 模拟首屏资源
- 10MB 视频切片(SATA SSD 上)→ 模拟流媒体
- 100MB 归档包(HDD 上)→ 模拟下载
逐个压测(例如 wrk -t4 -c1000 -d30s http://ip/1mb.bin),记录 QPS、延迟 P95、CPU 使用率。重点对比:相同并发下,HDD 文件的延迟抖动是否显著高于 NVMe?P95 是否拉长 3 倍以上?QPS 是否随文件增大而断崖下跌? 若是,问题不在 sendfile,而在 HDD 无法支撑高并发随机预读,需调整 vm.vfs_cache_pressure 或启用 open_file_cache 缓存 inode/stat。
关键避坑提醒
以下配置会直接让 sendfile 在异构环境中“失效”或放大差异:
- 开启
gzip on:强制走用户态压缩,彻底退出零拷贝路径 - 启用
range请求(如视频拖拽):sendfile 仅支持整文件或对齐 offset,非对齐 range 会 fallback 到 read+write - 未配
tcp_nopush on:小文件高频发送导致大量微包,掩盖存储差异,却放大网络栈开销 - 忽略
open_file_cache:HDD 上频繁 open/stat 加剧寻道,使冷读延迟雪上加霜











