@@ -868,15 +868,124 @@ df -i /dev/sfa1
868
868
869
869
870
870
# # 磁盘I/O
871
+ 磁盘是可以持久化存储的设备,根据存储介质的不同,常见磁盘可以分为两类:机械磁盘(Hard Disk Driver)和固态磁盘(Solid State Disk)。
871
872
873
+ # ## 磁盘IO原理
874
+ 第一类,机械磁盘,也称为硬盘驱动器(Hard Disk Driver),通常缩写为 HDD。机械磁盘主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中。在读写数据前,需要移动读写磁头,定位到数据所在的磁道,然后才能访问数据。
872
875
876
+ 显然,如果 I/O 请求刚好连续,那就不需要磁道寻址,自然可以获得最佳性能。这其实就是我们熟悉的,连续 I/O 的工作原理。与之相对应的,当然就是随机 I/O,它需要不停地移动磁头,来定位数据位置,所以读写速度就会比较慢。
877
+
878
+ 第二类,固态磁盘(Solid State Disk),通常缩写为 SSD,由固态电子元器件组成。固态磁盘不需要磁道寻址,所以,不管是连续 I/O,还是随机 I/O 的性能,都比机械磁盘要好得多。
879
+
880
+ 其实,无论机械磁盘,还是固态磁盘,相同磁盘的随机 I/O 都要比连续 I/O 慢很多,原因也很明显。
881
+ - 对机械磁盘来说,我们刚刚提到过的,由于随机 I/O 需要更多的磁头寻道和盘片旋转,它的性能自然要比连续 I/O 慢。
882
+ - 而对固态磁盘来说,虽然它的随机性能比机械硬盘好很多,但同样存在“先擦除再写入”的限制。随机读写会导致大量的垃圾回收,所以相对应的,随机 I/O 的性能比起连续 I/O 来,也还是差了很多
883
+ - 此外,连续 I/O 还可以通过预读的方式,来减少 I/O 请求的次数,这也是其性能优异的一个原因。很多性能优化的方案,也都会从这个角度出发,来优化 I/O 性能。
884
+
885
+ 机械磁盘和固态磁盘还分别有一个最小的读写单位
886
+ 机械磁盘的最小读写单位是扇区,一般大小为 512 字节。而固态磁盘的最小读写单位是页,通常大小是 4KB、8KB 等
887
+
888
+
889
+ # ### 通用块层
890
+ 跟我们上一节讲到的虚拟文件系统 VFS 类似,为了减小不同块设备的差异带来的影响, Linux 通过一个统一的通用块层,来管理各种不同的块设备。
891
+
892
+ 通用块层,其实是处在文件系统和磁盘驱动中间的一个块设备抽象层。它主要有两个功能
893
+ - 第一个功能跟虚拟文件系统的功能类似。向上,为文件系统和应用程序,提供访问块设备的标准接口;向下,把各种异构的磁盘设备抽象为统一的块设备,并提供统一框架来管理这些设备的驱动程序。
894
+ - 第二个功能,通用块层还会给文件系统和应用程序发来的 I/O 请求排队,并通过重新排序、请求合并等方式,提高磁盘读写的效率。
895
+
896
+ 其中,对 I/O 请求排序的过程,也就是我们熟悉的 I/O 调度。事实上,Linux 内核支持四种 I/O 调度算法,分别是 NONE、NOOP、CFQ 以及 DeadLine。这里我也分别介绍一下
897
+
898
+ 1. 第一种 NONE ,更确切来说,并不能算 I/O 调度算法。因为它完全不使用任何 I/O 调度器,对文件系统和应用程序的 I/O 其实不做任何处理,常用在虚拟机中(此时磁盘 I/O 调度完全由物理机负责)
899
+ 2. 第二种 NOOP ,是最简单的一种 I/O 调度算法。它实际上是一个先入先出的队列,只做一些最基本的请求合并,常用于 SSD 磁盘。
900
+ 3. 第三种 CFQ(Completely Fair Scheduler),也被称为完全公平调度器,是现在很多发行版的默认 I/O 调度器,它为每个进程维护了一个 I/O 调度队列,并按照时间片来均匀分布每个进程的 I/O 请求。类似于进程 CPU 调度,CFQ 还支持进程 I/O 的优先级调度,所以它适用于运行大量进程的系统,像是桌面环境、多媒体应用等
901
+ 4. 最后一种 DeadLine 调度算法,分别为读、写请求创建了不同的 I/O 队列,可以提高机械磁盘的吞吐量,并确保达到最终期限(deadline)的请求被优先处理。DeadLine 调度算法,多用在 I/O 压力比较重的场景,比如数据库等
902
+
903
+ 通用块层是 Linux 磁盘 I/O 的核心。向上,它为文件系统和应用程序,提供访问了块设备的标准接口;向下,把各种异构的磁盘设备,抽象为统一的块设备,并会对文件系统和应用程序发来的 I/O 请求,进行重新排序、请求合并等,提高了磁盘访问的效率。
873
904
874
- # ## 磁盘IO原理
875
905
# ### 磁盘管理
876
906
# ### 磁盘类型
877
907
# ### 磁盘接口
878
908
# ### 磁盘I/O栈
909
+ 我们可以把 Linux 存储系统的 I/O 栈,由上到下分为三个层次,分别是文件系统层、通用块层和设备层。这三个 I/O 层的关系如下图所示,这其实也是 Linux 存储系统的 I/O 栈全景图。
910
+ ! [[Pasted image 20250627182100.png]]
911
+ 图片来源 [https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram](https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram)
912
+
913
+ 文件系统层,包括虚拟文件系统和其他各种文件系统的具体实现。它为上层的应用程序,提供标准的文件访问接口;对下会通过通用块层,来存储和管理磁盘数据
914
+ 通用块层,包括块设备 I/O 队列和 I/O 调度器。它会对文件系统的 I/O 请求进行排队,再通过重新排序和请求合并,然后才要发送给下一级的设备层。
915
+ 设备层,包括存储设备和相应的驱动程序,负责最终物理设备的 I/O 操作。
916
+
917
+
879
918
# ## 性能指标
919
+ 说到磁盘性能的衡量标准,必须要提到五个常见指标,也就是我们经常用到的,使用率、饱和度、IOPS、吞吐量以及响应时间等。这五个指标,是衡量磁盘性能的基本指标。
920
+
921
+ - 使用率,是指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈。
922
+ - 饱和度,是指磁盘处理 I/O 的繁忙程度。过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求。
923
+ - IOPS(Input/Output Per Second),是指每秒的 I/O 请求数。
924
+ - 吞吐量,是指每秒的 I/O 请求大小。
925
+ - 响应时间,是指 I/O 请求从发出到收到响应的间隔时间。
926
+ 这里要注意的是,使用率只考虑有没有 I/O,而不考虑 I/O 的大小。换句话说,当使用率是100% 的时候,磁盘依然有可能接受新的 I/O 请求。
927
+
928
+ 不要孤立地去比较某一指标,而要结合读写比例、I/O 类型(随机还是连续)以及I/O 的大小,综合来分析。
929
+ 举个例子,在数据库、大量小文件等这类随机读写比较多的场景中,IOPS 更能反映系统的整体性能;而在多媒体等顺序读写较多的场景中,吞吐量才更能反映系统的整体性能。
930
+
931
+ # ### 磁盘I/O观测工具
932
+ - fio 来测试磁盘的 IOPS、吞吐量以及响应时间等核心指标。
933
+ - iostat 是最常用的磁盘 I/O 性能观测工具,它提供了每个磁盘的使用率、IOPS、吞吐量等各种常见的性能指标,当然,这些指标实际上来自 /proc/diskstats。
934
+ ` ` ` bash
935
+ # -d -x 表示显示所有磁盘I/O的指标
936
+ [~]$ iostat -d -x 1
937
+ Linux 5.14.0-427.13.1.el9_4.x86_64 (package.testocpdc1.shdc1.com) 06/27/2025 _x86_64_ (16 CPU)
938
+
939
+ Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s wrqm/s %wrqm w_await wareq-sz d/s dkB/s drqm/s %drqm d_await dareq-sz f/s f_await aqu-sz %util
940
+ dm-0 0.67 164.15 0.00 0.00 21.02 245.03 25.00 973.25 0.00 0.00 3.48 38.93 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.10 0.65
941
+ sda 0.00 0.00 0.00 0.00 2.89 29.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
942
+ sdb 0.70 164.18 0.08 10.28 21.51 233.21 10.29 973.25 15.37 59.90 7.21 94.55 0.00 0.00 0.00 0.00 0.00 0.00 2.41 0.33 0.09 0.67
943
+ sr0 0.00 0.00 0.00 0.00 0.07 42.08 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
944
+ ` ` `
945
+
946
+ ! [[Pasted image 20250627184607.png]]
947
+
948
+ - %util ,就是我们前面提到的磁盘 I/O 使用率;
949
+ - r/s+ w/s ,就是 IOPS;
950
+ - rkB/s+wkB/s ,就是吞吐量;
951
+ - r_await+w_await ,就是响应时间
952
+
953
+ 在观测指标时,也别忘了结合请求的大小( rareq-sz 和 wareq-sz)一起分析。
954
+
955
+ 你可能注意到,从 iostat 并不能直接得到磁盘饱和度。事实上,饱和度通常也没有其他简单的观测方法,不过,你可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,跟基准测试的结果(比如通过 fio)进行对比,综合评估磁盘的饱和情况。
956
+
957
+ # ### 进程I/O观测
958
+ 除了每块磁盘的 I/O 情况,每个进程的 I/O 情况也是我们需要关注的重点
959
+ 上面提到的 iostat 只提供磁盘整体的 I/O 性能数据,缺点在于,并不能知道具体是哪些进程在进行磁盘读写。要观察进程的 I/O 情况,你还可以使用 pidstat 和 iotop 这两个工具
960
+
961
+ pidstat 是我们的老朋友了,这里我就不再啰嗦它的功能了。给它加上 -d 参数,你就可以看到进程的 I/O 情况,如下所示:
962
+ ` ` ` bash
963
+ [~]$ pidstat -d
964
+ Linux 5.14.0-427.13.1.el9_4.x86_64 (ubuntu) 06/27/2025 _x86_64_ (16 CPU)
965
+
966
+ 06:49:45 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
967
+ 06:49:45 PM 1001 277531 0.00 0.00 0.00 0 systemd
968
+ 06:49:45 PM 1001 385588 0.00 0.00 0.00 0 bash
969
+ 06:49:45 PM 1001 456994 0.05 530.71 119.63 0 bash
970
+ 06:49:45 PM 1001 818504 0.42 2.76 0.00 0 bash
971
+ 06:49:45 PM 1001 1438525 0.02 0.78 0.00 0 bash
972
+ 06:49:45 PM 1001 1447221 0.00 0.00 0.00 0 bash
973
+ 06:49:45 PM 1001 1599497 10.63 16.63 0.03 0 java
974
+ 06:49:45 PM 1001 2038965 0.00 0.00 0.00 0 bash
975
+ 06:49:45 PM 1001 2540993 0.00 0.00 0.00 0 bash
976
+ ` ` `
977
+ 块 I/O 延迟(iodelay),包括等待同步块 I/O 和换入块 I/O 结束的时间,单位是时钟周期。
978
+
979
+ 除了可以用 pidstat 实时查看,根据 I/O 大小对进程排序,也是性能分析中一个常用的方法。这一点,我推荐另一个工具, iotop。它是一个类似于 top 的工具,你可以按照 I/O大小对进程排序,然后找到 I/O 较大的那些进程。
980
+ ` ` ` bash
981
+ $ iotop
982
+ Total DISK READ : 0.00 B/s | Total DISK WRITE : 7.85 K/s
983
+ Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s
984
+ TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
985
+ 15055 be/3 root 0.00 B/s 7.85 K/s 0.00 % 0.00 % systemd-journald
986
+ ` ` `
987
+
988
+
880
989
# ### 使用率
881
990
# ### IOPS
882
991
# ### 吞吐量
0 commit comments