Linux文件系统

文件系统本身是对存储设备上的文件进行组织管理的机制。组织方式不同就形成不同的文件系统。 在Linux中一切皆为文件,为了方便管理Linux文件系统为每个文件都分配两个数据结构,索引节点、和目录项。

  • 索引节点 用来记录文件的元数据如inode编号、文件大小、访问权限等,inode跟文件内容一样都会被持久化到磁盘中
  • 目录项 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。

文件目录索引节点文件数据关系

磁盘在执行文件系统格式化的时候会被划分成三个存储区域:

  • 超级块 存储整个文件系统的状态
  • 索引节点区 用来存储索引节点
  • 数据块区 则用来存储文件数据

Linux为了支持不同的文件系统,内核在用户进程和文件系统的中间引入了一个抽象层,VFS。 VFS定义了一组所有文件系统都支持的数据结构和标准接口。

文件系统和存储的关系

文件读写方式的各种差异导致I/O的分类多种多样。缓冲与非缓冲I/O、直接与非直接I/O、阻塞和非阻塞I/O、同步与异步I/O等。

  • 缓冲I/O 利用标准库缓存来加速文件的访问

  • 非缓冲I/O 直接通过系统调用来访问文件,不再经过标准库缓存

  • 直接I/O 跳过操作系统的页缓存,直接跟文件系统交互访问文件

  • 非直接I/O 经过系统的页缓存,然后有内核或者系统调用真正写入磁盘

  • 阻塞I/O 应用程序执行I/O操作后没有获得响应就会阻塞当前线程

  • 非阻塞I/O 应用程序执行I/O操作后不会阻塞当前线程,执行其它任务,随后通过轮询或者事件通知的形式获得调用结果

  • 同步I/O 程序执行I/O操作之后,要一直等到整个I/O完成后才能获得I/O响应

  • 异步I/O 程序执行I/O操作之后,不用等待完成和完成后的响应,而是继续执行。等到这次I/O完成后,响应会用事件通知的方式告知应用程序

磁盘IO

在Linux中磁盘实际上是作为一个块设备来管理的,也就是以块为单位读写数据,并且支持随机读写。每个块设备都会被赋予两个设备号, 分别是主、次设备号。主设备号主要用于驱动程序中,用来区分设备类型;而次设备号则是用来给多个同类设备编号

通用块层

和VFS类似,为了减少不同设备的差异带来的影响,Linux通过一个统一的通用块层来管理不同的设备。其实就是处在文件系统和磁盘中间的一个块设备抽象层,主要有以下两个功能:

  • 向上为文件系统和应用程序提供访问块的标准接口,向下把各个异构的磁盘设备统一抽象,并提供统一框架来管理这些设备的驱动程序
  • 给文件系统和应用程序发来的I/O请求排队,并通过重新排序、请求、合并等方式提高磁盘读写效率

Linux四种I/O调度算法

  • NONE NONE确切等说不算调度算法,因为它完全不实用任何I/O调度器,对文件系统和应用程序的I/O其实不做任何处理,常在虚拟机中(此时磁盘I/O调度完全由物理机负责)
  • NOOP NOOP最简单的一种I/O调度算法,实际上是一个先入先出的队列,只做一些最基本的请求合并,常用于SSD磁盘
  • CFQ(Completely Fair Scheduler),被称为完全公平调度器,是现在很多发行版的默认 I/O 调度器,它为每个进程维护了一个 I/O 调度队列,并按照时间片来均匀分布每个进程的 I/O 请求
  • DeadLine 调度算法,分别为读、写请求创建了不同的 I/O 队列,可以提高机械磁盘的吞吐量,并确保达到最终期限(deadline)的请求被优先处理。DeadLine 调度算法,多用在 I/O 压力比较重的场景,比如数据库等

总结I/O栈

把 Linux 存储系统的 I/O 栈,由上到下分为三个层次,分别是文件系统层、通用块层和设备层。

  • 文件系统层,包括虚拟文件系统和其他各种文件系统的具体实现。它为上层的应用程序,提供标准的文件访问接口;对下会通过通用块层,来存储和管理磁盘数据
  • 通用块层,包括块设备 I/O 队列和 I/O 调度器。它会对文件系统的 I/O 请求进行排队,再通过重新排序和请求合并,然后才要发送给下一级的设备层
  • 设备层,包括存储设备和相应的驱动程序,负责最终物理设备的 I/O 操作

磁盘性能指标

五个常见指标,使用率、饱和度、IOPS、吞吐量以及响应时间等。这五个指标,是衡量磁盘性能的基本指标。

  • 使用率,是指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈
  • 饱和度,是指磁盘处理 I/O 的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求
  • IOPS(Input/Output Per Second),是指每秒的 I/O 请求数
  • 吞吐量,是指每秒的 I/O 请求大小
  • 响应时间,是指 I/O 请求从发出到收到响应的间隔时间

iostat性能指标解读

分析磁盘I/O瓶颈过程

  1. 先用 iostat 发现磁盘 I/O 性能瓶颈
  2. 再借助 pidstat ,定位出导致瓶颈的进程
  3. 随后分析进程的 I/O 行为
  4. 最后,结合应用程序的原理,分析这些 I/O 的来源

分析过程以及工具

I/O性能优化思路

应用程序优化

应用程序处于整个I/O栈的最上端,它可以通过系统调用来觉得I/O模式,同时它也是I/O数据的最终来源,在上帝视角中可以根据以下几个点来进行优化

  1. 可以用追加写代替随机写,减少寻址开销,加快I/O写的速度
  2. 可以借助缓存I/O,充分利用系统缓存,降低实际I/O的次数
  3. 可以在应用程序内部构建自己的缓存,或者用 Redis 这类外部缓存系统。这样,一方面,能在应用程序内部,控制缓存的数据和生命周期;另一方面,也能降低其他应用程序使用缓存对自身的影响
  4. 在需要频繁读写同一块磁盘空间时,可以用mmap代替read/write,减少内存的拷贝次数
  5. 在需要同步写的场景中,尽量将写请求合并,而不是让每个请求都同步写入磁盘,即可以用fsync()取代O_SYNC
  6. 在多个应用程序共享相同磁盘时,为了保证I/O不被某个应用完全占用,推荐你使用cgroups的I/O子系统,来限制进程/进程组的IOPS以及吞吐量
  7. 在使用CFQ调度器时,可以用ionice来调整进程的I/O调度优先级,特别是提高核心应用的I/O优先级。ionice支持三个优先级类:Idle、Best-effort和Realtime。其中,Best-effort和Realtime 还分别支持0-7的级别,数值越小,则表示优先级别越高

文件系统优化

应用程序访问普通文件时,实际是由文件系统间接负责,文件在磁盘中读写,所以选择合适的文件系统也是优化的一个重要方面

  • 根据实际场景选择合适的文件系统
  • 选择好文件系统之后根据文件系统配置进一步调优
  • 优化文件系统缓存
  • 优化内核回收目录项和索引节点缓存

磁盘优化

  • 替换高性能的磁盘
  • 根据磁盘和应用的I/O特征选择合适的I/O调度算法
  • 对数据进行磁盘级别的隔离
  • 增大磁盘预读大小
  • 优化内核设备I/O选项,适当增加队列长

网络I/O

网络I/O主要延迟时由:服务器响应时间+带宽限制+网络延迟+跳转路由延迟+本地接收延迟