(本文是原理篇,后续还会陆续推出实战篇。)
我们应该使用什么样的方法和工具,来“快准狠”地定位系统的 I/O 瓶颈呢?今天我们就来聊一聊这个话题,首先我们来了解一下存储硬件相关的基本知识。
存储性能(存储金字塔)
这里的延迟我们大概可以看到一个量级,但是具体的延迟还需要看产品规格。
此外由于 RAID 卡的引入,不同 RAID 方式下 I/O 性能也会不一样。
CXL:https://zhuanlan.zhihu.com/p/567210344
一些基本概念
尺寸外形:也就是设备的形状和大小,通常存储设备的尺寸外形包括如下:
- 2.5寸或者3.5寸驱动器(在SFF标准中定义)
- M.2 和 PCI Express(PCIe)(在PCI-SIG标准中定义)
接口:也就是设备如何与计算机通信。常见的存储设备接口包括:
- SATA接口,通常用于2.5寸和3.5寸硬盘,有时候一些M.2设备也会使用
- PCI Express(PCIe)接口, 用于M.2和PCIe设备
- SAS(串行SCSI)和FC(Fibre Channel)接口,仅用于服务器领域和数据中心
PCIe 接口要比 SATA 接口快得多,SATA3 最大带宽是 6Gb/s,而基于 4X PCIe 的 M.2 接口最大可以达到 32Gb/s。
协议:定义了如何在计算机与设备之间传输数据。常见的协议包括:
- 用于SATA接口的AHCI或者ATA协议
- 用于 PCIe 接口的 NVMe 协议
HDD
磁盘结构
注意硬盘本身也有缓存。
盘片
一个磁盘(如一个 1T 的机械硬盘)由多个盘片(如下图中的 0 号盘片)叠加而成。
盘片的表面涂有磁性物质,这些磁性物质用来记录二进制数据。因为正反两面都可涂上磁性物质,故一个盘片可能会有两个盘面。
磁道、扇区
每个盘片被划分为一个个磁道,每个磁道又划分为一个个扇区。如下图:
其中,最内侧磁道上的扇区面积最小,因此数据密度最大。
柱面
每个盘面对应一个磁头。所有的磁头都是连在同一个磁臂上的,因此所有磁头只能“共进退”。
所有盘面中相对位置相同的磁道组成柱面。如下图:
磁盘的物理地址
CHS:Cylinder(柱面)、Head(磁头)、Sector(扇区)
由上,可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”。
在“文件的物理结构”小节中,我们经常提到文件数据存放在外存中的几号块(逻辑地址),这个块号就可以转换成(柱面号,盘面号,扇区号)的地址形式。
可根据该地址读取一个“块”,操作如下:
① 根据“柱面号”移动磁臂,让磁头指向指定柱面;
② 激活指定盘面对应的磁头;
③ 磁盘旋转的过程中,指定的扇区会从磁头下面划过,这样就完成了对指定扇区的读/写。
I/O(Input/Ouput)操作
单个IO:操作系统内核发出一个读 IO 命令,当控制磁盘的控制器接到这个指令后,控制器会给磁盘发送一个读数据的指令,并同时将要读取数据块的地址传送给磁盘,然后硬盘读取数据传送给控制器,并由控制器返回给操作系统,完成一个 IO 操作;
读写 IO:写磁盘为写 IO,读数据为读 IO;
随机访问(Random Access) 与顺序访问(Sequential Access):由此 IO 给出的扇区地址与上次 IO 结束的扇区地址相差是否较大决定
队列 IO 模式(Queue Mode)/并发 IO 模式(Burst Mode): 由磁盘组一次能执行的IO 命令个数决定;
完整的 IO 操作
当控制器对硬盘发出一个IO操作指令的时候,磁盘的磁头臂带动读写磁头离开着陆区,然后移动到要操作初始数据块所在的磁道正上方,此过程为寻道,消耗的时间为寻道时间;
磁头等到盘片旋转到初始数据块所在扇区的正上方,此时才能进行数据的读取,这个过程称之为旋转时间;
然后读取相应数据,直到完成这次IO所操作的全部数据,这个过程所花费的时间称之为数据传送时间。
硬盘关键指标
寻道时间
全程寻道时间:磁头横跨整个磁盘的宽度所用的时间(着陆区 --> 最外层 0 磁道);
平均寻道时间:一般为全程寻道时间的1/3;
道间寻道时间:磁头在相邻磁道之间所用的时间。
旋转时延
决定于主轴的转动速度
平均旋转动延迟:完全旋转用时的一半
5400 rpm 磁盘平均旋转时延:5.5ms15000 rpm 磁盘的平均旋转时延:2.0ms
数据传输时延
数据传输时延决定于数据传输速度,即单位时间内传输的数据量;
内部传输速度:数据从盘片扇区上传送到硬盘上的内部缓存的速度;
外部传输速度:接口的标称速度。
带宽(Throughput)
带宽是指磁盘在实际使用的时候从磁盘系统总线上流过的数据量,也称为磁盘的实际传输速率;
带宽 = IOPS * IO大小。
IOPS
IOPS是IO系统每秒所执行IO操作的次数,是一个重要的用来衡量系统IO能力的参数,对于单个磁盘,计算其完成一次IO所需要的时间来推算其IOPS
IO Time = 寻道时间 + 60s/转速/2 + IOChunkSize/传输速度
IOPS = 1/IOTime = 1 / (寻道时间 + 60s/转速/2 + IOChunkSize/传输速度)
单个IO大小 |
寻道时间(ms) |
旋转延迟(ms) |
c传输时延(ms) |
IO服务时间(ms) |
IOPS |
---|---|---|---|---|---|
4K | 5 | 2 | 4K/40MB = 0.1 | 7.1 | 140 |
8K | 5 | 2 | 8K/40MB = 0.2 | 7.2 | 139 |
16K | 5 | 2 | 16K/40MB = 0.4 | 7.4 | 135 |
32K | 5 | 2 | 32K/40MB = 0.8 | 7.8 | 128 |
当单次 IO 越小的时候,单次 IO 所耗费的时间也越少,相应的 IOPS 也就越大
磁盘利用率
平均响应时间(TR)=服务时间(TS)/(1 - 利用率)
其中 TS 是控制器处理一次 I/O 所用的时间,利用率是当前读写请求的数量与磁盘最大吞吐量的比值。
通常,在磁盘利用率达到70%之后,磁盘读写请求的延迟会出现比较明显的增大。
注意:iostat 等工具观测到的磁盘利用率,计算逻辑如下:
https://github.com/sysstat/sysstat/blob/master/iostat.c#L803简单讲就是从 /proc/diskstats 得到设备的“TimeSpentDoingIO”值,然后和观测时间做比较
硬盘性能计算
磁盘服务时间(Ts)是衡量磁盘性能的一个关键指标。Ts和磁盘利用率(U)决定过了一个应用的 I/O 响应时间。总的磁盘服务时间(Ts)是寻道时间(T)、旋转延迟(L)和内存传输时间(X)的总和。
Ts = T + L + X
给定如下磁盘实例,磁盘的具体规格计算如下:
在一个随机 I/O 的环境中,平均寻道时间为 5ms,因此,T = 5ms;
磁盘旋转速率为 15000 rpm(250转/秒) - 由此可以确定旋转延迟(L),大概是旋转一圈时间的一半,即 L=(0.5/250 rps,以 ms 为单位);
内部传输速率为 40MB/s,其内部传输时间(X)可以根据 I/O 块大小来计算。例如块大小为 32KB,那么 X=32KB/40MB。
由此,I/O控制器服务一个大小为32KB的块所用的时间为:
Ts = 5ms + 0.5 / 250 + 32KB/40MB = 7.8ms
每秒钟最大的 I/O 服务次数,即 IOPS 为 1/Ts = 1000 / 7.8 = 128 IOPS。
块大小 |
Ts = T + L + X |
IOPS = 1/Ts |
---|---|---|
4 KB | 5 ms + (0.5/250 rps) + 4 K/40 MB = 5 + 2 + 0.1 = 7.1 | 140 |
8 KB | 5 ms + (0.5/250 rps) + 8 K/40 MB = 5 + 2 + 0.2 = 7.2 | 139 |
16 KB | 5 ms + (0.5/250 rps) + 16 K/40 MB = 5 + 2 + 0.4 = 7.4 | 135 |
32 KB | 5 ms + (0.5/250 rps) + 32 K/40 MB = 5 + 2 + 0.8 = 7.8 | 128 |
64 KB | 5 ms + (0.5/250 rps) + 64 K/40 MB = 5 + 2 + 1.6 = 8.6 | 116 |
性能指标
描述 I/O 的性能指标有哪些?我们可以根据文件系统和磁盘 I/O 的原理,结合下面这张 Linux 系统的 I/O 栈图,来整理一下。
一个常见的 I/O 软件栈
Linux 文件系统缓存
Linux Block I/O 软件栈
其中 IO 调度器可以参考:Linux IO Scheduler: noop/deadline/cfq/bfq 到 blk-mq
站在操作系统的视角,等待时长指的是在块服务层调度器队列和设备分发队列中等待的时间。
服务时长指的是从向设备发布请求到请求完成的时间。这可能也包括在设备自带队列中等待的时间。
请求时长是指从 I/O 进入操作系统队列到请求完成的总时长。在这里,请求 时长是最重要的指标,因为在同步 I/O 中这就是应用必须等待的时间。
请求时长 = 等待时长 + 服务时长
文件系统 I/O 性能指标
我们先来看文件系统的情况。首先是存储空间的使用情况,包括容量、使用量以及剩余空间等。我们通常也称这些为磁盘空间的使用量,因为文件系统的数据最终还是存储在磁盘上。不过要注意,这些只是文件系统向外展示的空间使用,而非在磁盘空间的真实用量,因为文件系统的元数据也会占用磁盘空间。而且,如果配置了 RAID,从文件系统看到的使用量跟实际磁盘的占用空间,也会因为 RAID 级别的不同而不一样。
除了数据本身的存储空间,还有一个容易忽略的是索引节点的使用情况,它也包括容量、使用量以及剩余量等三个指标。如果文件系统中存储过多的小文件,就可能碰到索引节点容量已满的问题。
其次是缓存使用情况,包括页缓存、目录项缓存、索引节点缓存以及各个具体文件系统(如 ext4、XFS 等)的缓存。这些缓存会使用速度更快的内存,用来临时存储文件数据或者文件系统的元数据,从而可以减少访问慢速磁盘的次数。
除了以上这两点,文件 I/O 也是很重要的性能指标,包括 IOPS(包括 r/s 和 w/s)、响应时间(延迟)以及吞吐量(B/s)等。在考察这类指标时,通常还要考虑实际文件的读写情况。比如,结合文件大小、文件数量、I/O 类型等,综合分析文件 I/O 的性能。
诚然这些性能指标非常重要,但不幸的是,Linux 文件系统并没提供,直接查看这些指标的方法。我们只能通过系统调用、动态跟踪或者基准测试等方法,间接进行观察、评估。不过,实际上,这些指标在我们考察磁盘性能时更容易见到,因为 Linux 为磁盘性能提供了更详细的数据。
磁盘 I/O 性能指标
磁盘相关的有四个核心的磁盘 I/O 指标:
使用率,是指磁盘忙于处理 I/O 请求的百分比。过高的使用率(比如超过 60%)通常意味着磁盘 I/O 存在性能瓶颈。
IOPS(Input/Output Per Second),是指每秒的 I/O 请求数。
吞吐量,是指每秒的 I/O 请求大小。
响应时间,是指从发出 I/O 请求到收到响应的间隔时间。
考察这些指标时,一定要注意综合 I/O 的具体场景来分析,比如读写类型(顺序还是随机)、读写比例、读写大小、存储类型(有无 RAID 以及 RAID 级别、本地存储还是网络存储)等。我们不能把不同场景的 I/O 性能指标,直接进行分析对比。
除了这些指标外,缓冲区(Buffer)也是要重点掌握的指标,它经常出现在内存和磁盘问题的分析中。
总结
文件系统和磁盘 I/O 的这些指标都很有用,需要我们熟练掌握,以下是总结的图。
性能工具
掌握文件系统和磁盘 I/O 的性能指标后,我们还要知道,怎样去获取这些指标,也就是搞明白工具的使用问题。
我们可以根据场景选用不同的工具,例如:
第一,df 既可以查看文件系统数据的空间容量,也可以查看索引节点的容量。至于文件系统缓存,我们通过 /proc/meminfo、/proc/slabinfo 以及 slabtop 等各种来源,观察页缓存、目录项缓存、索引节点缓存以及具体文件系统的缓存情况。
第二,iostat 和 pidstat 可以观察磁盘和进程的 I/O 情况。它们都是最常用的 I/O 性能分析工具。通过 iostat ,我们可以得到磁盘的 I/O 使用率、吞吐量、响应时间以及 IOPS 等性能指标;而通过 pidstat ,则可以观察到进程的 I/O 吞吐量以及块设备 I/O 的延迟等。
第三,top 可以用于查看系统的 CPU 使用情况,如果发现 iowait 比较高,就可以用 iostat 发现是否存在磁盘的 I/O 的使用率瓶颈,并用 pidstat 找出进行大量 I/O 的进程;最后可以通过 strace 和 lsof,可以找出问题进程正在读写的文件,并最终锁定性能问题的来源。
性能指标和工具
从 I/O 指标出发,我们更容易把性能工具同系统工作原理关联起来,对性能问题有宏观的认识和把握。而从性能工具出发,可以更快上手使用工具,迅速找出我们想观察的性能指标。特别是在工具有限的情况下,我们更要充分利用好手头的每一个工具,少量工具也要尽力挖掘出大量信息。
第一个维度,从文件系统和磁盘 I/O 的性能指标出发。根据不同的性能指标,对提供指标的性能工具进行分类和理解。
第二个维度,从工具出发。也就是要知道这些工具能提供哪些指标。
具体到每个工具的使用方法,一般都支持丰富的配置选项。可以通过 man 命令,查它们的使用手册。
如何迅速分析 I/O 的性能瓶颈
从 I/O 角度来分析,最开始的分析思路基本上类似,都是:
先用 iostat 发现磁盘 I/O 性能瓶颈;
再借助 pidstat ,定位出导致瓶颈的进程;
随后分析进程的 I/O 行为;
最后,结合应用程序的原理,分析这些 I/O 的来源。
为了缩小排查范围,可以先运行那几个支持指标较多的工具,如 iostat、vmstat、pidstat 等。然后再根据观察到的现象,结合系统和应用程序的原理,寻找下一步的分析方向。
图中列出了最常用的几个文件系统和磁盘 I/O 性能分析工具,以及相应的分析流程,箭头则表示分析方向。这其中,iostat、vmstat、pidstat 是最核心的几个性能工具,它们也提供了最重要的 I/O 性能指标。
参考
《BPF 之巅》 第8/9章
极客时间 -《Linux 性能优化》