5000 个 1KB 文件搬运实测:你的 cp/rsync 对比,可能测的是内存而不是磁盘
5000 个 1KB 文件搬运实测:你的 cp/rsync 对比,可能测的是内存而不是磁盘
「小文件用 tar 管道比 cp 快」「rsync 比 cp 慢」——这类结论在网上流传很广,但大多数实测都有一个共同缺陷:没清页缓存。第二次跑的命令测的是内存带宽,不是磁盘。我用同一台机器、同一批文件,把热缓存和冷缓存分开测了一遍,结论和流传的版本不太一样。
实验设置
- 5000 个 1KB 文件,分散在 50 个目录里,总计 5MB
- ext4(
/dev/sda1),每次跑 3 遍取中位数 - 冷缓存用
sync; echo 3 > /proc/sys/vm/drop_caches - 目标目录每次跑前清空,避免「覆盖已有文件」混进结果
第一组:热缓存(大多数人实际在测的东西)
cp -r 0.133 s
tar -cf - | tar 0.125 s
cp -a 0.153 s
rsync -a 0.229 s
看起来 rsync 慢了 80%,tar 管道赢了 cp -a 约 20%。但请注意这批数字的绝对量级:0.13 秒搬完 5MB,折算 38 MB/s 以上——这个速度里磁盘几乎没参与,数据全在 page cache 里。
第二组:冷缓存(每个命令都从盘上读)
rsync -a 3.630 s (3.801 / 3.630 / 3.623)
cp -a 3.854 s (3.854 / 3.694 / 3.920)
tar -cf - | tar 3.920 s (3.920 / 3.982 / 3.919)
三个工具挤在 3.6–3.9 秒之间,最大差距 8%,而且顺序完全反了:热缓存下最慢的 rsync 在冷缓存下最快,热缓存下最快的 tar 管道排到了最后。
这不是工具的性能变了,而是你测的东西变了。冷缓存下瓶颈是「每个文件一次 open/stat/read」的元数据开销,谁少一次系统调用谁赢;热缓存下瓶颈是内存拷贝和进程调度,差距被放大成看起来很有意义的百分比。
同一批命令,换个缓存状态,冠军就换人。 所以任何不带 drop_caches 的 cp/rsync 横评,结论只在「源目录刚被写过」这个前提下成立。
第三组:元数据才是真正的成本
同样是搬 5MB:
1 个 5MB 文件 0.011 s
5000 个 1KB 文件 0.153 s
字节数一样,小文件慢了 14 倍。这不是 I/O 带宽问题,是 5000 次目录项查找、inode 分配、权限拷贝。推论很直接:如果你的目录里全是几十 KB 的小文件,优化方向不是换工具,而是减少文件数(打包、合并日志、归档冷数据),任何工具选择都只在 8% 这个量级上折腾。
第四组:增量同步,rsync 才真正拉开差距
前面三组 rsync 都不占优,但它的价值不在首次搬运。往已有副本里改 10 个文件,再同步一次:
rsync -a 0.071 s
cp -a 0.367 s
rsync 快 5 倍。原因有两个:它按 size+mtime 跳过未变文件;而 cp 不但全量重写,还要对已存在的目标文件做截断再写,比写新文件更慢——注意 cp -a 在这一组里从 0.153 秒涨到了 0.367 秒,同一命令、同一数据量,只因目标已存在。
怎么选
- 一次性搬大目录、源数据刚写过:
tar -C src -cf - . | tar -C dst -xf -,简单且不比谁慢 - 重复同步(部署、备份、重跑流水线):
rsync -a,增量场景 5 倍差距,前面那 8% 不值得计较 - 文件数极多(十万级):先想能不能打包,
tar归档比任何逐文件拷贝都快一个数量级 - 测性能:先
sync再drop_caches,跑 3 次取中位数。否则你测的是内存速度,还会得出互相矛盾的结论
最后一个提醒:drop_caches 是全局操作,会把整机缓存清空,生产机上别随手跑。要测就测在隔离环境里——我这次是在一块只放测试数据的 scratch 目录上做的,跑完顺手删掉,不留在业务盘上。
- 点赞
- 收藏
- 关注作者
评论(0)