文件系统
文件系统工作原理
文件系统是在磁盘的基础上,提供了一个用来管理文件的树状结构。接下来看一下Linux文件系统的工作原理。
索引节点和目录项 在 Linux 中一切皆文件 ,文件系统,本身是对存储设备上的文件,进行组织管理的机制
为了方便管理,Linux 文件系统为每个文件都分配两个数据结构, 索引节点(index node) 和 目录项(directory entry) 。它们主要用来记录文件的 元信息 和 目录结构 。
- 索引节点,简称为 inode,用来记录文件的元数据,比如 inode 编号、文件大小、访问权限、修改日期、数据 的位置等。索引节点和文件一一对应,它跟文件内容一样,都会被持久化存储到磁盘中。所以记住,索引节 点同样占用磁盘空间。
- 目录项,简称为dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。不过,不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。
文件数据到底是怎么存储的呢? 磁盘读写的最小单位是扇区,然而扇区只有512B大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为4KB,也就是由连续的8个扇区组成。
注意:
- 目录项本身就是一个内存缓存,而索引节点则是存储在磁盘中的数据 ,Buffer和Cache缓存可以协调慢速磁盘与快速CPU的性能差异,文件内容会缓存到页缓存 Cache中 ,这些索引节点自然也会缓存到内存中,加速文件的访问。
- 磁盘在执行文件系统格式化时,会被分成三个存储区域, 超级块、 索引节点区 和数据块区 。
- 超级块,存储整个文件系统的状态。
- 索引节点区,用来存储索引节点。
- 数据块区,则用来存储文件数据。
虚拟文件系统 为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层,也就是虚拟文件 系统 VFS(Virtual File System)。VFS 定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,只需要跟 VFS 提供的统一接口进行交互就可以了,而不需要再关心底层各种文件系统的实现细节。 为了更好地理解系统调用、VFS、缓存、文件系统以及块存储之间的关系 ,可以参考Linux 文件系统的架构图
通过这张图,你可以看到,在 VFS 的下方,Linux 支持各种各样的文件系统,如 Ext4、XFS、NFS 等等。按照存储位 置的不同,这些文件系统可以分为三类。
第一类是基于磁盘的文件系统,也就是把数据直接存储在计算机本地挂载的磁盘中。常见的 Ext4、XFS、OverlayFS 等,都是这类文件系统。
第二类是基于内存的文件系统,也就是我们常说的虚拟文件系统。这类文件系统,不需要任何磁盘分配存储空间, 但会占用内存。我们经常用到的 /proc就是一种最常见的虚拟文件系统。
第三类是网络文件系统,也就是用来访问其他计算机数据的文件系统,比如 NFS、SMB、iSCSI 等。
这些文件系统,要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件。拿第一类,也 就是基于磁盘的文件系统为例,在安装系统时,要先挂载一个根目录(/),在根目录下再把其他文件系统(比如 其他的磁盘分区、/proc 文件系统、/sys 文件系统、NFS 等)挂载进来。
IO栈
Linux的IO路径可能是Linux系统中比较复杂的模块,它直接决定了系统的性能。
根据这张 I/O 栈的全景图,我们可以更清楚地理解,存储系统 I/O 的工作原理。
应用程序 : 这没什么好说的,通过相关系统调用(如open/read/write)发起IO请求,属于IO请求的源头;
文件系统 : 应用程序的请求直接到达文件系统层。文件系统又分为VFS和具体文件系统(ext3、ext4等),VFS对应用层提供统 一的访问接口,而ext3等文件系统则实现了这些接口。另外,提高IO性能,在该层还实现了诸如page cache等功 能。同时,用户也可以选择绕过page cache,而是直接使用direct模式进行IO(如数据库)。
块设备层 : 文件系统将IO请求打包提交给块设备层,该层会对这些IO请求作合并、排序、调度等,然后以新的格式发往更底层。在该层次上实现了多种电梯调度算法,如cfq、deadline等。
SCSI层 : 块设备层将请求发往SCSI层,SCSI就开始真实处理这些IO请求,但是SCSI层又对其内部按照功能划分了不同层次: * SCSI高层:高层驱动负责管理disk,接收块设备层发出的IO请求,打包成SCSI层可识别的命令格式,继续往下发; * SCSI中层:中层负责通用功能,如错误处理,超时重试等; * SCSI低层:底层负责识别物理设备,将其抽象提供给 高层,同时接收高层派发的scsi命令,交给物理设备处理。
性能指标
磁盘I/O性能指标
这里需要了解磁盘 I/O 性能监控的指标,以及每个指标的所揭示的磁盘某方面的性能。磁盘 I/O 性能监控的指标主 要包括:
-
每秒 I/O 数( IOPS ) 一次磁盘的连续读或者连续写称为一次磁盘 I/O, 磁盘的 IOPS 就是每秒磁盘连续读次数和连续写次数之和。
-
吞吐量( Throughput) 指硬盘传输数据流的速度,传输数据为读出数据和写入数据的和。
-
平均 I/O 数据尺寸 平均 I/O 数据尺寸为吞吐量除以 I/O 数目,该指标对揭示磁盘使用模式有重要意义。
-
磁盘活动时间百分比( Utilization) %util 磁盘处于活动时间的百分比,即磁盘利用率,磁盘在数据传输和处理命令处于活动状态。
-
服务时间( ServiceTime) svctm 指磁盘读或写操作执行的时间,包括寻道,数据传输等时间。
-
I/O 等待队列长度( Queue Length) 指待处理的 I/O 请求的数目,如果 I/O 请求压力持续超出磁盘处理能力,该值将增加。
-
等待时间( Wait Time) 指磁盘读或写操作等待执行的时间,即在队列中排队的时间。
磁盘 I/O 观测
iostat
是最常用的磁盘 I/O 性能观测工具,它提供了每个磁盘的 使用率 、 IOPS 、 吞吐量 等各种常见的性能指标,
当然,这些指标实际上来自 /proc/diskstats。
- 命令格式:
iostat [参数][时间][次数]
-
常用参数 -c : 仅显示 cpu的状态
-d : 显示磁盘使用情况,不可以和 -c一起使用
-k : 默认显示的是读入读出的 block信息,以 KB 为单位显示 -m : 以 M 为单位显示
-t : 显示终端和CPU的信息
-x : 显示详细信息 -
例.显示所有磁盘I/O的指标
- 参数解释 参数 | 含义 | 说明 | | -------- | ------------------------ | ---------------------------------- | | r/s | 每秒发送给磁盘的读请求数 | 合并后的请求数 | | w/s | 每秒发送给磁盘的写请求数 | 合并后的请求数 | | rkB/s | 每秒从磁盘读取的数据量 | 单位为KB | | wkB/s | 每秒向磁盘写入的数据量 | 单位为KB | | rrqm/s | 每秒合并的读请求数 | %rrqm标识合并读请求的百分比 | | wrqm/s | 每秒合并的写请求数 | %wrqm标识合并写请求的百分比 | | r_await | 读请求处理完毕等待时间 | 包括队列中的等待时间和 设备实际处理时间,单位毫秒 | | w_await | 写请求处理完成等待时间 | 包括队列中的等待时间和 设备实际处理时间,单位毫秒 | | aqu-sz | 平均请求队列长度 | 旧版为avgqu-sz | | rareq-sz | 平均读请求大小 | 单位为KB | | wareq-sz | 平均写请求大小 | 单位为KB | | svctm | 处理I/O请求所需要的平均时间(不包含等待时间) | 单位为毫秒。 | | %util | 磁盘处理I/O的时间百分比 | 使用率,由于可能存在并行I/O 100% 并不一定代表磁盘I/O饱和
-
需要注意的指标
1.%util ,就是我们前面提到的磁盘 I/O 使用率;
2.r/s+ w/s ,就是 IOPS;
3.rkB/s+wkB/s ,就是吞吐量;
4.r_await+w_await ,就是响应时间;
5.iostat 并不能直接得到磁盘饱和度。可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,跟基 准测试的结果进行对比,综合评估磁盘的饱和情况。
进程 I/O 观测
要观察进程的 I/O 情况,你还可以使用 pidstat 和 iotop 这两个工具。
pidstat 观测I/O pidstat 给它加上 -d 参数(使用-d选项,我们可以查看进程IO的统计信息 ), 你就可以看到进程的 I/O 情况,如下所示:
从 pidstat 的输出你能看到,它可以实时查看每个进程的 I/O 情况,包括下面这些内容。
- 用户 ID(UID)和进程 ID(PID) 。
- 每秒读取的数据大小(kB_rd/s) ,单位是 KB。
- 每秒发出的写请求数据大小(kB_wr/s) ,单位是 KB。
- 每秒取消的写请求数据大小(kB_ccwr/s) ,单位是 KB。
- 块 I/O 延迟(iodelay),包括等待同步块 I/O 和换入块 I/O 结束的时间,单位是时钟周期。
iotop 观测I/O iotop是一个类似于 top 的工具,你可以按照 I/O 大小对进程排序,然后找到 I/O 较大的那些进程。
如果没有该命令,请通过 yum install iotop 进行安装
这个输出,你可以看到,前两行分别表示,进程的磁盘读写大小总数和磁盘真实的读写大小总数。因为缓存、缓 冲区、I/O 合并等因素的影响,它们可能并不相等。
剩下的部分,则是从各个角度来分别表示进程的 I/O 情况,包括线程 ID、I/O 优先级、每秒读磁盘的大小、每秒写 磁盘的大小、换入和等待 I/O 的时钟百分比等。
磁盘I/O优化策略
应用程序优化策略
- 可以用追加写代替随机写,减少寻址开销,加快 I/O 写的速度。
- 可以借助缓存 I/O ,充分利用系统缓存,降低实际 I/O 的次数。
- 可以在应用程序内部构建自己的缓存,或者用 Redis 这类外部缓存系统。
- 需要频繁读写同一块磁盘空间时,可以用 mmap 代替 read/write,减少内存的拷贝次数。
- 在需要同步写的场景中,尽量将写请求合并,而不是让每个请求都同步写入磁盘。
- 在多个应用程序共享相同磁盘时,为了保证 I/O 不被某个应用完全占用,推荐你使用 cgroups 的 I/O 子系 统,来限制进程/进程组的 IOPS 以及吞吐量。
文件系统优化策略
- 可以根据实际负载场景的不同,选择最适合的文件系统。比如 Ubuntu 默认使用 ext4 文件系统,而CentOS7 默认使用xfs文件系统;
- 在选好文件系统后,还可以进一步优化文件系统的配置选项;
- 可以优化文件系统的缓存。
磁盘优化策略 磁盘也是整个 I/O 栈的最底层。从磁盘角度出发,自然也有很多有效的性能优化方法
-
最简单有效的优化方法,就是换用性能更好的磁盘,比如用 SSD 替代 HDD;
-
可以使用RAID,把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列。这样做既可以提高数据的 可靠性,又可以提升数据的访问性能;
-
针对磁盘和应用程序 I/O 模式的特征,我们可以选择最适合的 I/O 调度算法。比方说,SSD 和虚拟机中的磁 盘,通常用的是 noop 调度算法。而数据库应用,我更推荐使用 deadline 算法;
-
我们可以对应用程序的数据,进行磁盘级别的隔离;
-
在顺序读比较多的场景中,我们可以增大磁盘的预读数据;
-
我们可以优化内核块设备 I/O 的选项。比如,可以调整磁盘队列的长度,以提升磁盘的吞吐量。