性能基础之硬盘性能知识必知必会
一、常见硬盘分类
硬盘有很多种,常用的是机械硬盘(HDD)和固态硬盘(SSD)。这里简单回顾下,机械硬盘是传统普通硬盘,它的构成主要由盘片,磁头、盘片转轴、控制电机、磁头控制器等部件构成,读写速度和转速(通常上万转)相关;而SSD盘是以固态电子存储芯片阵列组成,包括如闪存芯片、控制芯片、缓存芯片等。其实还有混合硬盘,在机械硬盘上加以闪存颗粒作为缓存以提升性能。
典型的HDD和SSD示意图:
(图片来自网络)
由于其结构的不同,二者在性能等多方面也差异巨大。一般来说,SSD比HDD机械硬盘在访问性能上是数十倍,能耗(质量、体积、功耗)和可靠性(防震、耐温)各方面上也有很大提升,所以现在的PC/笔记本等大多已选择了SSD固态硬盘。而HDD硬盘一般在容量、成本和寿命上更有优势。
SSD 的种类很多,按照技术来说有单层(SLC)和多层(MLC,TLC 等)。按照质量和性能来分,有企业级和普通级。根据安装的接口和协议来分,有 SAS、SATA、PCIe 和 NVMe 等。
二、基础原理知识
硬盘 I/O 其实是挺复杂的一个逻辑,但我们今天只说在做性能分析的时候,应该如何定位问题。在这之前需要先理解一下文件的一个重要的属性:inode。
什么是 inode 呢?先来看一个示意图:
磁盘上最小的存储单元是扇区 sector,每8个扇区组成一个块 I/O Block Size(4096字节)。如下所示:
[root@7DGroup2 ~]# tune2fs -l /dev/vda1|grep Block
Block count: 10485504
Block size: 4096
Blocks per group: 32768
[root@7DGroup2 ~]#
文件的存储就是由这些块组成的,当块多了之后就成了如下这样(其实磁盘上的块比这个图中多得多,这里只是示意图):
其中红色的这部分是存储的文件,我们通常在文件系统中直接ls或者用其他命令操作文件的时候是根据路径来操作的,那些是上层的命令。当我们执行了一个命令之后,操作系统会来找到这些文件做相应的操作,怎么找到这些文件呢,那就需要 inode 了。
Inode 用来存储这些文件的元信息,也就是索引节点,它包括的信息有:
- 字节数
- User ID
- Group ID
- 读、写、执行权限
- 时间戳,共有三个:ctime 指 inode 上一次变动的时间,mtime 指文件内容上一次变动的时间,atime 指文件上一次打开的时间
- 链接数,有多少文件名指向这个 inode
- 文件数据 block 的位置
通过这些信息,我们才能实现对文件的操作。这个 inode 其实也是存储在磁盘上的,也需要占用一些空间,如上图中的绿色部分所示。
当我们在系统级看到 IO 过高的时候,比如下图所示:
从上图可以看到,这系统几乎所有的 CPU 都在等 IO。这时怎么办?就用我们后面提到的分析的思路,查看进程级和线程级的 IO,进而找到具体的文件。
三、常见性能指标
对于I/O密集型系统,其性能指标最重要的是以下三个:
- 吞吐量(Throughput):每秒的读写数据量,衡量的是存储系统的实际数据传输速率,通常以 MB/s 或 GB/s 为单位。类似如:吞吐率、带宽、传输率等。
- 时延(Latency):I/O 操作的发送到接收确认所经过的时间,常以为毫秒单位,对这一性能指标,我们通常会考虑它的平均值和高位百分数,比如 P99、P95。类似如:响应时间、请求时间等。
- IOPS(I/O per second):每秒读/写次数,单位为次(计数),这是衡量存储性能的主要指标之一。每个 IO 的请求都有自己的特性,比如读还是写,是顺序读写还是随机读写,IO 的大小是多少等。类似如:系统并发量、每秒请求QPS等。
一般来讲,IOPS 与吞吐量是紧密相关的。长时间的同类任务,通常系统吞吐量高更重要;短时间的随机任务,系统的时延小更重要,而在时延保证的前提下,IOPS 越高系统的服务能力越强。
另外,读写块大小(I/O Block Size)是非常重要的因素。在具体的性能分析中,吞吐量和IOPS有如下关系:
注:吞吐量等于 IOPS 和 IO 读写块大小的乘积。
这个也很容易理解,比如对一个硬盘的读写 IO 是 1MB,硬盘的 IOPS 是 100,那么硬盘总的吞吐量就是 100 MB/s,需要强调的是,这里 IO 的具体特性很重要,比如是顺序还是随机,IO 大小等。
所以在分析最大吞吐量和最大IOPS时,需要针对地选择BlockSize;提升BlockSize,通常会使系统吞吐量提升,系统IOPS下降。
另外,最大峰值性能(maximum performance)不同于可持续性能(sustained performance),所以在性能分析时需要维持一段时间的稳定负载并统计平均值。
还有一点要注意,有些系统会因为其 IO 队列深度增加,而获得更好的 IO 性能;比如吞吐量会升高,平均访问时延会降低。这是因为存储系统的 IO 队列处理机制,可以对 IO 进行重新排序,从而获得好的性能。比如,它可以合并几个相邻的 IO,把随机 IO 重新排序为顺序 IO 等。
以下为不同硬盘类别性能指标参考数据:
四、IO 性能分析思路
构建 I/O 分析决策树:
先聊下磁盘系统结构:
- 如果是IDE驱动器,磁盘命名为:hda、hdb、hdc等;
- 如果是SCSI驱动器,磁盘命名为:sda、sdb、sdc等
- 磁盘通常被分成多个分区,分区设备的名称是通过将分区号添加到基本设备名称的末尾来创建的,每个单独的分区通常包含一个文件系统或一个交换分区,按照
/etc/fstab
中的指定,这些分区被装载到 Linux 根文件系统中。这些挂载的文件系统包含应用程序读写的文件。 - 当应用程序执行读或写操作时,Linux 内核可能会将文件的副本存储在其缓存或缓冲区中,并在不访问磁盘的情况下返回所请求的信息。但是,如果 Linux 内核没有存储在内存中的数据副本,它会向磁盘的 I/O 队列添加一个请求。如果 Linux 内核注意到多个请求请求磁盘上的连续位置,它会将它们合并为一个大请求。这种合并消除了第二个请求的寻道时间,从而提高了总体磁盘性能。当请求被放入磁盘队列时,如果磁盘当前不忙,它将开始为 I/O 请求提供服务。如果磁盘正忙,请求将在队列中等待,直到驱动器可用,然后对其进行服务。
在这一层咱们主要关注 I/O ,既然是关注 I/O ,如果 I/O 高应该怎么去分析?怎么定位?
vmstat [-D] [-d] [-p 分区]
参数说明:
- d:显示磁盘相关统计信息。
- D: 显示 Linux I/O 子系统的总统计信息,统计数据是自系统启动以来的总数
- p: 统计数据是自系统启动以来的总数,而不仅仅是此示例和上一个示例之间发生的总数。
[root@ ~]# vmstat 1 10
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 261712 144188 2935000 0 0 2 12 74 84 1 1 99 0 0
0 0 0 261728 144188 2935032 0 0 0 84 1244 2384 1 1 98 0 0
0 0 0 261484 144188 2935032 0 0 0 8 1508 2439 1 1 98 0 0
0 0 0 261612 144188 2935032 0 0 0 0 1270 2558 2 1 98 0 0
0 0 0 261604 144188 2935032 0 0 0 0 1111 2134 1 1 98 0 0
0 0 0 261604 144188 2935032 0 0 0 0 1222 2401 1 1 99 0 0
0 0 0 261480 144188 2935032 0 0 0 0 1515 2951 2 1 97 0 0
0 0 0 260688 144188 2935032 0 0 0 12 1466 2569 2 2 97 0 0
0 0 0 261064 144188 2935032 0 0 0 0 1828 3418 3 2 95 0 0
0 0 0 261232 144188 2935032 0 0 0 0 1089 2100 1 0 99 0 0
使用 vmstat 中关于磁盘也就是 bo/bi/wa:
- bo 这表示在前一间隔内写入磁盘的块总数。(在vmstat中,磁盘的块大小通常为1024字节)
- bi 显示在上一间隔中从磁盘读取的块数。(在vmstat中,磁盘的块大小通常为1024字节)
- wa 表示等待I/O完成所花费的CPU时间,每秒写入磁盘块的速率。
在 Linux 操作系统中 I/O 分析最常见的命令是 iostat, 这个工具可以查看进程发出 IO 请求的数量、系统处理 IO 请求的耗时、磁盘的利用率等,也可以分析进程与操作系统的交互过程中 IO 方面是否存在瓶颈。
# iostat [参数] [时间] [次数]
iostat -d -x -k 1 10
参数说明:
- -c:显示 CPU 使用情况
- -d:显示磁盘使用情况
- -k:以 K 为单位显示
- -m:以 M 为单位显示
- -N:显示磁盘阵列(LVM) 信息
- -n:显示 NFS 使用情况
- -p:可以报告出每块磁盘的每个分区的使用情况
- -t:显示终端和 CPU 的信息
- -x:显示详细信息
计数器信息的含义:
- rrqm/s:每秒这个设备相关的读取请求有多少被 Merge了(当系统调用需要读取数据的时候,VFS 将请求发到各个 FS,如果 FS 发现不同的读取请求读取的是相同 Block 的数据,FS会将这个请求合并Merge)
- wrqm/s:每秒这个设备相关的写入请求有多少被 Merge 了
- r/s:每秒读取数
- w/s:每秒写入数
- rKB/s:每秒向设备发出的读请求数
- wKB/s:每秒向设备发出的写请求数
- avgrq-sz:平均请求扇区的大小
- avgqu-sz:是平均请求队列的长度。毫无疑问,队列长度越短越好。
- await:每一个 I/O 请求的处理的平均时间(单位是微秒毫秒)。这里可以理解为 I/O 的响应时间,一般系统 I/O 响应时间应该低于 5ms,如果大于 10ms就比较大了。这个时间包括了队列时间和服务时间,也就是说,一般情况下,await 大于 svctm,它们的差值越小,则说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
- w_await:表示写入的平均响应时间
- r_await:表示读取的平均响应时间
- svctm:表示平均每次设备 I/O 操作的服务时间(以毫秒为单位)。如果 svctm 的值与 await 很接近,表示几乎没有 I/O 等待,磁盘性能很好,如果 await 的值远高于 svctm 的值,则表示 I/O 队列等待太长,系统上运行的应用程序将变慢。这个数可以参考,但不一定准
- %util:在统计时间内所有处理 I/O 时间,除以总共统计时间。例如,如果统计间隔1秒,该设备有 0.8 秒在处理 I/O,而 0.2 秒闲置,那么该设备的
%util = 0.8/1 = 80%
,所以该参数暗示了设备的繁忙程度。一般地,如果该参数是 100 %表示设备已经接近满负荷运行了(当然如果是多磁盘,即使 %util 是 100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)
常见用法:
iostat -d -k 1 10 #查看 TPS 和吞吐量信息(磁盘读写速度单位为 KB)
iostat -d -m 2 #查看 TPS 和吞吐量信息(磁盘读写速度单位为 MB)
iostat -d -x -k 1 10 #查看设备使用率(%util)、响应时间(await)
iostat -c 1 10 #查看 cpu 状态
iostat -c 1 10 #查看 cpu 状态
注意点:
- 网卡的大吞吐量可能导致更多的 cup
- 大量的 cup 开销又会增加更多内存使用请求
- 大量内存与磁盘的请求可能导致更多的 cpu 以及 IO 问题
主要关注计数器:
- util
- avgqu-sz
- await
- svtm
- r/s
- w/s
出现瓶颈的计数器:
- %util 很高(接近100%)
- await 远大于 svctm(差值越大,队列时间越长)
- avgqu-sz 比较大(队列长度越短越好)
- cpu > wa 过大 (参考值超过 20)
- system > bi&bo 过大(参考值超过 2000)
而 IO/s(IOPS) 的关键计算示例是这样的:
IO/s = r/s + w/s = 18.33+114.33 = 132.66
%util = ( (IO/s * svctm) /1000) * 100% = 100.02564%
这个%util是用svctm算来的,既然svctm都不一定准了,那这个值也只能参考了。还好我们还有其他工具可以接着往深了去定位,所以,接下来,我们得查一下是什么程序写的。这里我们用 iotop 命令查看:
Total DISK READ : 2.27 M/s | Total DISK WRITE : 574.86 M/s
Actual DISK READ: 3.86 M/s | Actual DISK WRITE: 34.13 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
394 be/3 root 0.00 B/s 441.15 M/s 0.00 % 85.47 % [jbd2/vda1-8]
32616 be/4 root 1984.69 K/s 3.40 K/s 0.00 % 42.89 % kube-controllers
13787 be/4 root 0.00 B/s 0.00 B/s 0.00 % 35.41 % [kworker/u4:1]
...............................
从上面的Total DISK WRITE/READ就可以知道当前的读写到底有多少了,默认是按照I/O列来排序的,这里有Total,也有Actual,并且这两个并不相等,为什么呢?
因为 Total 的值显示的是用户态进程与内核态进程之间的速度,而 Actual 显示的是内核块设备子系统与硬件之间的速度。
(图片来自《性能测试实战 30 讲》)
而在I/O交互中,由于存在cache和在内核中会做I/O排序,因此这两个值并不会相同。那如果你要说磁盘的读写能力怎么样,我们应该看的是Actual。这个没啥好说的,因为Total再大,不能真实写到硬盘上也是没用的。
在以上的线程列表中,通过排序,就可以知道是哪个线程(注意在第一列是 TID 哦)占的I/O高了。
参考资料:
- 高楼《性能测试实战 30 讲》
- 高楼《性能分析之从 IO 高定位到具体文件》
- 《linux 磁盘IO测试工具:fio (同时简要介绍dd工具测试)》
- 点赞
- 收藏
- 关注作者
评论(0)