打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
为什么单个大文件比总体积相同的多个小文件复制起来要快很多?

比如说,在我的电脑上(windows操作系统,NTFS文件系统),复制一个1G的文件到磁盘的另一个分区所花的时间比复制1024个1M的文件到磁盘的另一个分区所用的总时间要小很多。

听说过上古时代的“磁盘碎片整理程序”吗?见过chkdsk的类似输出吗?


我们知道,机械硬盘的盘片被划分为一圈圈的同心圆环,数据就存在圆环上,这就是所谓的“磁道”;每个圆环又被分成若干小段,这些小段被称为“扇区”——最开始的扇区容量是512字节,现在硬盘容量越来越大,扇区大小就涨到了4k。

扇区是硬盘存储单元的最小分配单位;文件数据就存储在这些扇区上。

那么,如何知道哪些扇区对应于哪些文件呢?尤其是,如果一个文件是不停续写的(比如日志),续写过程中(因为空间不足)另外一些文件被删了,那么这个文件在磁盘上实际存储时,它的后半部分可能反而会存储于前半部分之前。这该怎么办?

简单。链表式管理——文件名存于文件分配表,然后用一个指针指向文件内容所在的第一个扇区;然后呢,next指针别往第一个扇区放,那样就太不方便了,继续按文件分配表指定的规则记到文件分配表里吧。

说白了就是个变形的链表:“传统”链表node节点最后放next,这种链表干脆开个next数组,里面的next[0]\next[1]等元素就指出了数据的存储位置。

当然,实际场景不可能这么简单粗暴,而是必须做很多很多的优化——然后这个链表的具体结构就变得它妈都不认识了。

但思路还是这个思路,完全可以按这个思路分析其表现。


前面提到的“磁盘碎片整理程序”实际上就对应于这种“简单粗暴版链表”的一个固有缺陷:如果磁盘上文件很多、又经过了长时间使用,那么里面的存储空间很可能已经千疮百孔了:


那么当我们要存储一个很大的文件16的话,它就可能会占据4、7、9、10、12、14号空间——当然,基于链表算法,我们看来文件内容仍然是连续的;但物理上就是这么断断续续。

这种断断续续会引来什么问题呢?

没错,频繁的寻道问题。

比如,块4位于磁道7的第9个扇区的话,我们就要先把磁头移动到磁道7;然后等待磁盘旋转,直到把第9个扇区送到磁头下——此时,块4的内容才能读出来。

然后,接下来要读块7,但块7却在磁道13的第173个扇区——没说的,先移动磁头、再等等盘片旋转到位吧。

你看,为了读完这个文件,我们不得不寻道6次、等待磁盘旋转6次——每次都需要十几个毫秒。

但如果文件能连续存储的话,我们只需寻道一次、等待磁盘旋转一次,然后一次性读出所有文件内容——这是不是就能节约6个十几毫秒?

极端情况下,一个文件过于支离破碎时,寻道时间可能要比实际的数据读取时间高几十上百甚至上千倍。

我们把这种“简单粗暴的链表式管理法”所导致的文件存储空间支离破碎现象叫做“磁盘碎片”——不是磁盘碎了,而是我们的文件存储的太散乱了,以至于在寻道上浪费了过多时间!

为了对付这个问题,微软不得不开发了“磁盘碎片整理程序”,尽量把同一个文件的内容挪到一起、按顺序存储到一整片磁盘空间里——这样只需一次寻道,然后一口气读出整个文件就是(实际上很难做到;但也没必要做到,只要别让寻道严重拖慢文件读取速度就行)。

而更高明的管理方案,比如Linux的ext2/3/4、微软的ntfs,它们会从一开始就合理划分磁盘空间、自动为大文件分配连续空间——换句话说,从一开始就自动避免磁盘碎片的产生。

因此,过去有一句很难懂的话:磁盘整理是愚蠢的FAT文件系统给自己挖的坑,设计水平到了的文件系统压根不需要什么“磁盘碎片整理”。

现在,你大概能明白这句话说的是什么了。


知道了这个预备知识,那么你的问题就很好回答了:

单个1G的文件,在现代文件系统里,可能至多需要十次不到的寻道就能全部读取;而1024个1M的细小文件呢,那就相当于FAT时代、存在一千个文件碎片的单个大文件——起码多了一千次寻道呢,怎么可能不慢。

事实上,这样算还是算少了。因为磁盘驱动必须先读取文件分配表,然后才能拿到“指针”、才能去按照“指针”指示寻找对应的扇区;那么对1G的大文件,文件分配表寻道一次,扇区寻道一次,差不多就够了;而对1M的一堆小文件呢,文件分配表寻道1024次,每个文件的首扇区又要寻道1024,实际多了两千多次寻道。

按每次寻道需要15ms算,额外就需要15X2000=30000毫秒,也就是30秒时间;而现在的硬盘在顺序读写时平均读写速率可以飙到170M/s以上;哪怕按100M/s算,1G的文件10秒也能传完了;结果寻道愣是多寻了30秒——慢了足足3倍!

而且,这里仅仅算了读取;写入时,小文件也需要先写文件分配表后写内容,这显然又是一堆寻道……不过这个场景过于复杂(比如同个磁盘不同分区拷贝、不同磁盘之间拷贝等等,细节各有不同),就不深入分析了。


最后,很多答主提到“Linux的ext4无法重现这个问题”;这是因为Linux针对文件读写做了很多很多优化。

其中之一,就是把文件分配表里面的“next指针们”事先加载到内存——这在Linux下被称为inode(当然这只是个比喻说法,inode当然不可能仅仅是“next指针们”,甚至文件系统压根就不是简单的链表结构——前面已经说过了,拿链表打比方仅仅是为了方便理解)。

你进入某个目录,Linux就会加载所有文件的inode到内存;那么当你需要访问目录下某个文件时,它就不需要到文件分配表读取信息了,直接利用内存中的inode即可。这就节约了磁盘访问必需的时间。

这些inode甚至还有自己的引用计数——于是,在Linux下,你完全可以搞一个临时文件、fork出一组进程然后通过这个文件交换信息;为了确保在程序关闭甚至崩溃退出后删除这个临时文件,你甚至可以先打开这个文件、然后马上unlink删除它;此时内存中的inode引用计数不到零,它就仍然是可以正常访问的;只有持有这个inode的所有程序都关闭了、这个inode才会真正被删除。

因此,只要你理解了这个,Linux就不会留存“未清理的临时文件”,不需要像Windows那样经常清理temp目录下的垃圾。

借助缓存、尽量把同一个目录下的文件安排到一起(Linux可以把某个磁盘/分区挂载到某个目录下,同目录以同样的途径安置是必需项)、尽量把短时间内相续访问过的文件放到一块……

这甚至都不是刻意的优化:尽量缓存磁盘数据、尽量把同时提交的访问按顺序写入磁盘连续区域,这样写起来也快读起来也快,干嘛坑自己呢。

类似的,读取大量小文件时,既然inode本就在内存里,那么把所有读取请求排序(其实就是著名的电梯算法)、尽量一次寻道读出更多内容、优先寻道离当前磁道更近的磁道(磁头转动5°当然比转10°消耗时间更少),系统性能是不是就有了极大提升?

这些,都是Linux的常规操作。

借助这些,你尽可以乱糟糟的访问,它会帮你安排的清清楚楚;于是1G大文件和1024个1M的小文件复制就没有太大差别了。


最后,现在普遍用了SSD硬盘。这种硬盘不存在寻道问题,因此随机访问性能可以碾压机械硬盘。

和机械硬盘相反,这种硬盘为了避免单个区域过多写入造成损坏、降低使用寿命,恰恰要故意“制造碎片”——或者说,尽量把数据分散开来、让整个磁盘的每个区域雨露均沾。

对这种硬盘,以上分析就牛头不对马嘴了。

当然,系统有无缓存文件分配表(inode),最终结果的差别仍然不小。毕竟内存访问的速度、延迟还是比SSD优秀太多。但这个差异往往需要专业的测试方案才能发现,并不是凭感觉就能感觉出来的。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
存储系统讲解——文件系统介绍
Linux文件系统十问,你知道吗?
操作系统之文件管理,万字长文让你彻底弄懂
整理磁盘碎片
硬盘数据恢复入门教程(逻辑盘BPB表篇)
硬盘出错、数据丢失怎么办?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服