做运维的估计都知道使用ext3文件系统时删除大文件很慢,那么大家有没有想过为什么呢?我也有过同样的疑问,于是查了相关资料并找到了一些理由。
在ext系列的文件系统中有一个很重要的概念inode(它与文件独立存在),它维护了文件的相关属性信息。
struct ext3_inode { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* 文件大小,单位是 byte */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Creation time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* blocks 计数 */ __u32 i_flags; /* File flags */ __u32 l_i_reserved1; /* 可以忽略 */ __u32 i_block[EXT3_N_BLOCKS]; /* 一组 block 指针 */ __u32 i_generation; /* 可以忽略 */ __u32 i_file_acl; /* 可以忽略 */ __u32 i_dir_acl; /* 可以忽略 */ __u32 i_faddr; /* 可以忽略 */ __u8 l_i_frag; /* 可以忽略 */ __u8 l_i_fsize; /* 可以忽略 */ __u16 i_pad1; /* 可以忽略 */ __u16 l_i_uid_high; /* 可以忽略 */ __u16 l_i_gid_high; /* 可以忽略 */ __u32 l_i_reserved2; /* 可以忽略 */ };
从它的数据结构就可以看出有atime,ctime,mtime(这几个名词是不是特别熟?因为经常会在find命令使用到它)。然后这里重点讨论就是i_block[EXT3_N_BLOCKS]这个数组,它记录了文件存在磁盘上的具体位置,里面很多block指针,它们指向了数据在磁盘上的位置。问题就出在这,当一个文件很大的时候,仅仅用这个i_block[EXT3_N_BLOCKS]无法表示(最大只能表示到EXT3_N_BLOCKS*block_size)以后,那么就只能通过嵌套的block指针,也就是里面的某个指针又是指向一个新的block[]这样就可以记录更大的文件。但是这样带来的后果却是删除大文件的操作会变得很慢。为什么呢?有两点:
1.刚才说了由于采用这种映射关系i_block[EXT3_N_BLOCKS],所以当文件很大,映射的层次就会增多,这也会增加相应操作的代价,比如说分配存储空间、删除数据文件。
2.因为我们在创建一系列文件的时候,对应inode的存储空间都是顺序分配的,这些inode都是顺序相邻的,但是对于这些文件本身则不是顺序的。这个就相当于数据库中的堆表的行和索引的关系,在索引中每个行按索引字段顺序存储的,但是他们对应的数据文件中的行却是随机(random)的(这里的行相当于文件系统中的一个文件,而索引里面的行则相当于inode),而我们在删除一个文件时,不仅要删除数据文件也要删除对应的inode,所以这就增加了随机查找的代价。
在第1点里面,是因为文件大导致删除文件慢,第2点则解释了文件多而导致删除文件慢
那ext4是怎么改进的呢?根据百科上对ext4的介绍,它与ext3一个很大的不同点在于使用了extent分配存储空间(是不是发现它很熟悉,因为Oracle、innodb里面都有这个概念),extent最大的优势在于连续分配,这样效率便会极大的提高。在其他方面的改进?参考百科~
/* * Structure of an inode on the disk */ struct ext4_inode { __le16 i_mode; /* File mode */ __le16 i_uid; /* Low 16 bits of Owner Uid */ __le32 i_size_lo; /* Size in bytes */ __le32 i_atime; /* Access time */ __le32 i_ctime; /* Inode Change time */ __le32 i_mtime; /* Modification time */ __le32 i_dtime; /* Deletion Time */ __le16 i_gid; /* Low 16 bits of Group Id */ __le16 i_links_count; /* Links count */ __le32 i_blocks_lo; /* Blocks count */ __le32 i_flags; /* File flags */ union { struct { __le32 l_i_version; } linux1; struct { __u32 h_i_translator; } hurd1; struct { __u32 m_i_reserved1; } masix1; } osd1; /* OS dependent 1 */ __le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */ __le32 i_generation; /* File version (for NFS) */ __le32 i_file_acl_lo; /* File ACL */ __le32 i_size_high; __le32 i_obso_faddr; /* Obsoleted fragment address */ union { struct { __le16 l_i_blocks_high; /* were l_i_reserved1 */ __le16 l_i_file_acl_high; __le16 l_i_uid_high; /* these 2 fields */ __le16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; struct { __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ __le16 m_i_file_acl_high; __u32 m_i_reserved2[2]; } masix2; } osd2; /* OS dependent 2 */ __le16 i_extra_isize; __le16 i_pad1; __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ __le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */ __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ __le32 i_crtime; /* File Creation time */ __le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */ __le32 i_version_hi; /* high 32 bits for 64-bit version */ };