解决NTFS拷贝文件远比磁盘物理读取速度慢的问题

问题

周末在Linux下搬运磁盘数据,发现一个很奇怪的现象。从通过ntfs-3g挂载的NTFS分区读取数据,写入到另外一个基于RAID1阵列的分区,理论上能到50MB/s+,实际上却只有20MB/s左右。而且在Windows上拷贝文件也是如此。这究竟是为何?

分析

先列一下设备情况。测试机器CPU是1037U,内存4G,通过HM76主板支持USB3.0。源磁盘是一块的WD My Book Essential 2TB USB3.0移动硬盘,分区是NTFS,跳过文件系统直接顺序读取速度曲线基本上从100MB/s+一路降低最后大概是50MB/s+。目的磁盘是由两块ST Expansion 4TB USB 3.0移动硬盘组成的软RAID1阵列再使用lvm在其上创建ext4逻辑卷,实际写入速度在最快110MB/s左右,最慢大概60MB/s左右。

通过iostat查看rsync拷贝文件时的现象,发现源磁盘读取的时候同时伴有写入操作,但是这块源磁盘除了拷贝数据进行读取不可能有其他进程在使用啊,那写入操作是哪里来的呢?

经过一番查找,我发现了一个之前从没注意过的事情,就是文件系统读取文件时,通常会伴随着写入。这是为什么? 原因在于文件包含的一个属性最后访问时间。当文件被读取时,通常会更新这个文件的最后访问时间属性,因此会伴随着写入,因此机械磁盘同时进行读写,破坏了原有连续的读操作,也可能伴随着重新寻道,因此慢了很多。

解决

方向确定解决方案就顺理成章,通过搜索,得知通过给mount时添加noatime参数,即可不会更新最后访问时间,最大程度上避免读取时进行写入。

这里有一点,就是默认mount时基本都使用relatime参数,即针对最后访问时间早于最后修改时间的文件,每天只更新一次。这最大化降低了频繁读情况下的性能,但是对于冷数据首次读取并无帮助。

重新挂载后用rsync进行拷贝,提升到了50MB/s+,考虑到这是一个几乎全满并且有大量小文件的分区,这个性能已经很不错了。本次问题成功解决~

延伸

事情到了这里,我突然想起了一个曾经困扰我多年的问题。2011年我获得了梦寐以求的W520(内置一块5400转的500G硬盘),虽然性能强大,但是开机时间基本上没有低于3分钟过。同盘拷贝文件最快速度基本都是20MB/s这个水平。而且长时间不用重新开机会更慢。到2016年的时候,尽管我做全力去优化和调整,开机时间已经到了6~8分钟这个级别。有幸当时赶上了SSD开始降价,2016年中花了不到300元买了一块120G的mSATA固态硬盘,把系统分区移动过去后,开机时间降到了30秒以内。然后我把原来的硬盘放到光驱位使用,此时从固态拷贝文件写入到机械磁盘速度基本上能到80MB/s~100MB/s。如果当时我分析并得出读取文件时,是既读又写导致读取性能严重降低,那我不至于忍受这么多年的龟速开机了。

Windows使用的NTFS,同样可以设置不在读取时更新最后访问时间字段,方法是修改注册表的键值,在Windows 10下修改HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem这个目录里的NtfsDisableLastAccessUpdate键,把值改为十六进制80000001,重启后即可避免读取文件时更新最后访问时间导致同时写入。总结一下这个操作带来的好处:

  • 对于一些使用机械硬盘作为系统盘的机器来说,能够很大程度优化读取性能,加快开机速度与文件拷贝速度。
  • 对于使用固态硬盘的机器,此操作仍具有意义。比如我的机器有自动备份功能,定期如一周会自动备份一次。这个操作也能减小备份时读取冷数据造成的额外写入动作,有助于长期减少固态硬盘写入量,延长固态硬盘寿命

因此带来的坏处,即某些应用可能依赖于最后访问时间这个属性做功能。我看到的说是某些邮件软件,还有安全软件。但是我并没有用到类似的邮件软件,在安全软件中我也把访问文件时引发扫描这这个特性关闭了(会严重降低软件读取的性能),所以我可以安全的取消这个特性。是否取不更新读取时间这个策略,最终还需要你根据实际情况来决定。

后记

世上不存在绝对好的事情,所谓优化也必然伴随代价。每个人针对自己的情况,权衡利弊后,做出最大化自己收益的选择。有得有失才是事物本来的样子。

参考