【天池比赛日记 - 1】稀疏文件和文件空洞

log

在Linux和Windows下,我们都曾试图通过fileChannel.map来使用稀疏文件构建<Long, Long><Long, String>磁盘map方案。发现,在Windows下,系统和JVM对稀疏文件和MappedByteBuffer支持的更好,在Linux下我们使用了6个线程并发插入(我们的方案是模拟ConcurrentHashMap完成的线程安全map)的情况下,才达到Windows下单线程的构建速度。显然,这是操作系统实现决定的。而且,发现在文件大小小于4GB时,Linux速度较为出色,但是大于4GB就远远逊色于Windows。

另外,我们试图提前构建好空白文件(写入全量的0),但并没有拯救我们的map在Linux下的性能。所以,也导致了后期切换了Map的方案(做到了200s并发插入3亿索引数据)(后面会详细介绍,我们map的实现过程,和实现多线程和Page Cache的思路)。

文件空洞

在unix文件操作中,文件位移量可以大于文件的当前长度。这种情况下,对该文件的下一次写将延长该文件,并在该文件中构成一个空洞。位于文件中,但没有写过的字节都被假设为0。

如果offset比文件的长度更大,下一个写操作就会将文件extend,造成文件空洞(hole)。没有实际写入文件的所有自己由重复的0表示,空洞是否占用磁盘由文件系统决定。

稀疏文件 Sparse File

和普通文件基本相同,区别在于文件中的部分数据是全0,这部分数据不需要占用磁盘空间。
1

inode数据块存储

索引节点采用了多重索引结构,体现在直接指针和三个间接指针,直接指针包含12个直接指针块,直接指向包含文件数据的数据块,紧跟在后面的三个间接指针是为了适应文件的大小变化而设计的

stat命令可以查看某个文件的inode信息

inode会消耗磁盘空间,所以os一般将硬盘分为两个区域,数据区和inode区。每个inode大小一般是128/256byte

sudo dumpe2fs -h /dev/hda | grep “Inode size” # 查看inode节点大小

由于每个文件都必须有一个inode,所以可能出现inode耗尽,但是磁盘未满的情况,此时已经无法创建新的文件。

2

文件系统存储稀疏文件时,inode索引节点中只给出实际占用磁盘空间的Block号,数据全0且不占用磁盘空间的文件Block没有物理磁盘Block号。

文件所占用的磁盘空间仍然是连续的

3

多重索引

Linux文件系统数据结构存放采用inode多重索引结构,有直接指针,和3个间接指针。

  • 直接指针直接指向保存出具的Block

    12个,若Block大小为4096Byte,则可以保存48KB文件

  • 一级指针指向一个Block,该Block中的数据是Block指针,指向真正数据Block

    假设每个指针占用4个字节,则一级指针指向Block可以保存4096/4个指针,即1024个Blocks,算出一级指针可以保存的文件数据大小为4MB

  • 二级和三级类推

    二级: (1024*1024)*4096=4GB ; 三级: (1024*1024*1024) * 4096 = 4TB