Kyle's Notebook

Linux I/O 原理与分析

Word count: 6.4kReading time: 24 min
2021/04/20

Linux I/O 原理与分析

  • I/O 性能指标
    • 文件系统
      • 存储空间容量、使用量和剩余空间
      • 索引节点容量、使用量和剩余量
      • 缓存
        • 页缓存
        • 目录项缓存
        • 索引节点缓存
        • 物理文件系统缓存
      • IOPS(文件 I/O)
      • 响应时间(延迟)
      • 吞吐量(B/s)
    • 磁盘
      • 使用率
      • IOPS
      • 吞吐量(B/s)
      • 响应时间(延迟)
      • 缓冲区
      • 相关因素
        • 读写类型(顺序或随机)
        • 读写比例
        • 读写大小
        • 存储类型(RAID 级别、本地或网络等·)

先附上 CPU、内存、磁盘、网络各种操作执行时间开销的直观对比:

操作 参考时间 对比基本单位
CPU 执行一般指令 0.37ns 1s
CPU 从一级缓存读取数据 0.5ns 1.3s
CPU 分支预测错误 5ns 13s
CPU 从二级缓存读取数据 7ns 18.2s
CPU 加/解互斥锁 25ns 65s
内存 寻址 100ns 4min
CPU 上下文切换 1500ns 65min
网络 1Gbps 网络上传输 2KB 20μs 14.4h
固态硬盘 随机访问 150μs 4.5d
内存 顺序读取 1MB 250μs 7.5d
网络 数据中心网络来回 0.5ms 15d
固态硬盘 顺序读取 1MB 1ms 1m
机械硬盘 寻址 10ms 10m
机械硬盘 顺序读取 1MB 20ms 20m
网络 世界上任意两地来回(平均) 150ms 12.5y
虚拟服务器 重启 5s 300y
物理服务器 重启 5min 25,000y

文件系统与磁盘

由于磁盘是 块设备,可被划分为不同的 分区,在磁盘或者分区上可再创建 文件系统,并挂载到系统的某个目录中,系统通过挂载目录来读写文件。

Linux 中一切皆文件,因此可通过相同的文件接口来访问磁盘和文件。磁盘读写的原理:磁盘读写的最小单位是扇区(512B),为了提高读写效率,文件系统把连续的扇区组成逻辑块,每次都以逻辑块(常见 4KB,即连续的 8 个扇区)为最小单元来管理数据。其有两种 I/O 方式:

  • 普通文件 :I/O 请求首先经过文件系统,由文件系统与磁盘交互。

  • 块设备文件:跳过文件系统、直接与磁盘交互,即 裸 I/O

两种 I/O 方式使用的缓存不同,文件系统管理的缓存是 Cache 的一部分,裸磁盘的缓存则是使用 Buffer。

文件系统

Linux 文件系统的四大基本要素:目录项、索引节点、逻辑块、超级块,接下来是具体的解析。

索引节点和目录项

文件系统 是对存储设备上的文件进行组织管理的机制。由于 Linux 中一切皆文件,除了普通的文件和目录,块设备、套接字、管道等也都通过统一的文件系统来管理,并使用两个数据结构记录文件元信息和目录结构:

  • 索引节点(index node,即 inode):用来记录文件的元数据,比如 inode 编号文件大小访问权限修改日期数据位置 等,与文件一一对应,并会被持久化存储到磁盘中(因此也占用磁盘空间)。

  • 目录项(directory entry,即 entry):用来记录 文件名称索引节点指针 以及 与其他目录项的关联关系(树状结构)。多个关联的目录项构成文件系统的目录结构。目录项是由内核维护的一个内存数据结构,通常也称为 目录项缓存

索引节点的容量是在格式化磁盘时设定好的,由格式化工具自动生成。当发现索引节点空间不足、但磁盘空间充足时,很可能就是由过多小文件导致,此时应删除小文件或移动到索引节点充足的其它位置。

目录项和索引节点的关系是多对一,一个文件可以有多个别名(比如建立硬链接为文件创建别名)。由于前者是内存缓存数据,后者是磁盘数据,为了协调磁盘与 CPU 的性能差异,文件内容会缓存到页缓存 Cache 中。

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

  • 超级块:存储整个文件系统的状态。

  • 索引节点区:存储索引节点。

  • 数据块区:存储文件数据。

img

虚拟文件系统(VFS)

即为了支持不同的文件系统,Linux 内核在用户进程和文件系统之间引入的抽象层。

VFS 定义一组所有文件系统都支持的数据结构和标准接口。用户进程和内核中的其他子系统与 VFS 提供的统一接口进行交互,而不需要再关心底层文件系统的实现细节。

img

VFS 支持的各种文件系统可分为三类:

  • 磁盘文件系统:把数据直接存储在计算机本地挂载的磁盘中,如 Ext4、XFS、OverlayFS 等。

  • 内存文件系统:即虚拟文件系统,不需要磁盘分配存储空间、但会占用内存。比如 /proc/sys(向用户空间导出层次化的内核对象)。

  • 网络文件系统:用于访问其他计算机数据的文件系统,如 NFS、SMB、iSCSI 等。

文件系统要先挂载到 VFS 目录树中的某个子目录(挂载点)才能访问其中的文件。比如在安装系统时,要先挂载一个根目录(/),在根目录下再把其他文件系统(其他的磁盘分区、/proc、/sys、NFS 等)挂载进来。

Page Cache

已知应用程序通过系统调用向内核发起 I/O 操作,在进入块 I/O 层之前有以下几种情况:

  • Memory-Mapped I/O:通过内存映射把数据写入 Page Cache。

  • Buffered I/O:由 VFS 处理系统调用,把数据写入 Page Cache。

  • Direct I/O:由 VFS 处理系统调用,不经过 Page Cache 直接写入块 I/O 层。

因此 Page Cache 是内核态内存,不属于用户。

文件系统 I/O

文件系统被挂载后,就可以通过挂载点访问其管理的文件。VFS 有标准的文件访问接口,以系统调用的方式提供给应用程序使用。比如 cat 命令,就是依次调用 open()read()write() 函数来执行:

1
2
3
int open(const char *pathname, int flags, mode_t mode); 
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

以读取磁盘文件内容、通过网卡发送的场景为例。涉及以下几个环节:

1
[磁盘文件] -> [PageCache](内核态)-> [用户缓冲区](用户态)-> Socket 缓冲区(内核态)-> [网卡]

其中从内核态到用户态再到内核态涉及 2 次拷贝,再加上读磁盘和写网卡一共 4 次拷贝。

由于用户进程缓冲区空间有限(比如 32KB),要发送一个大文件(假设 320MB)就需要 320MB / 32KB 共 10,000 次才能完成,即 40,000 次上下文切换。

直接与非直接 I/O

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

  • 非直接 I/O:文件读写时经过系统页缓存,再由内核或额外的系统调用真正写入磁盘。

在系统调用中指定 O_DIRECT 标志即实现直接 I/O(默认非直接 I/O)。直接 I/O、非直接 I/O,本质上还是和文件系统交互;在数据库等场景中也有裸 I/O,即跳过文件系统读写磁盘的情况。

零拷贝:属于非直接 I/O,其特点是无需经过用户缓冲区(用户态),即只有 2 次上下文切换,3 次内存拷贝;如果网卡支持 SG-DMA 技术,则无需 Socket 缓冲区,可以进一步优化为 2 次上下文切换和 2 次内存拷贝。

1
2
3
4
[磁盘文件] -> [Page Cache] -> Socket 缓冲区 -> [网卡]

SG-DMA:
[磁盘文件] -> [Page Cache] -> [网卡]

零拷贝有以下特点:

  • 开发者无需关心 Socket 缓冲区大小,把发送字节数设为文件未发送字节数(比如 320MB),则一次性经网卡发出的数据量为 Socket 缓冲区大小(比如 1.4MB),上下文切换次数和内存拷贝次数约为 2 * 320MB / 1.4MB 共 457 次,拷贝数据量也只有 640MB(耗费 CPU 资源更少)。

  • 利用 Page Cache:Page Cahce 基于 LRU 缓存最近访问的数据,效率远高于访问磁盘。再加上预读出更多数据,在淘汰出 Page Cache 前就被进程访问,发挥更大收益。

  • 使用零拷贝不允许进程在发送操作前对文件内容进行加工(比如压缩)。

  • 当文件太大,文件中某一部分内容被再次访问的概率非常低,却耗费 CPU 或 DMA 的多一次拷贝;而且大文件快速把 Page Cache 占满,其它热点小文件就无法利用 Page Cache。

直接 I/O 的应用场景:

  • 由应用程序实现磁盘文件缓存,避免 Page Cache 额外的性能消耗(比如 MySQL)。

  • 高并发下传输大文件,本身难以命中 Page Cache、带来额外的内存拷贝、挤占小文件 I/O 的内存,使用 直接 I/O 更合理。

除此之外,直接 I/O 无法享用基于 Page Cache 的 磁盘预读I/O 合并(即内核 I/O 调度试图在 Page Cache 中缓存尽量多的连续 I/O,在合并成大 I/O 后再一次发给磁盘以减少寻址操作)带来的性能提升,因此应用场景很有限。

阻塞与非阻塞 I/O

阻塞 / 非阻塞针对的是 I/O 调用者(即应用程序)。

  • 阻塞 I/O:应用程序执行 I/O 操作后如没有获得响应就会阻塞当前线程,不能执行其他任务。

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

比如访问管道或者网络套接字时,设置 O_NONBLOCK 标志就表示用非阻塞方式访问(默认阻塞,即 send() 操作的线程阻塞,无法去做其它事)。

如果使用 epoll,系统会告诉该套接字的状态,因此可以用非阻塞的方式使用。当这个套接字不可写时,可以转去做其它事,比如读写其它套接字。

同步与异步 I/O

同步 / 异步针对的是 I/O 执行者(即系统)。

  • 同步 I/O:应用程序发起 I/O 操作后要等到整个 I/O 完成后,才能获得 I/O 响应。

  • 异步 I/O:应用程序发起 I/O 操作后不等待完成,I/O 完成后响应由内核以事件通知应用程序。

操作文件时设置 O_DSYNC(等文件数据写入磁盘后才能返回)或 O_SYNC(在前者基础上要求文件元数据也要写入磁盘后才能返回)标志就代表同步 I/O。比如访问管道或者网络套接字时设置了 O_ASYNC 选项,相应的 I/O 就是异步 I/O,内核会再通过 SIGIO 或者 SIGPOLL,来通知进程文件是否可读写。

异步 I/O 解决了阻塞问题,但其实现上存在无法利用 PageCache 的缺陷,只支持直接 I/O。

1
[磁盘文件] -> [用户缓冲区](用户态)-> Socket 缓冲区(内核态)-> [网卡]

缓冲与非缓冲 I/O

缓冲指标准库内部实现的缓存,由于不论是否被标准库缓存,最终还是经过系统调用来访问文件,系统调用后再通过页缓存来减少磁盘的 I/O 操作。

  • 缓冲 I/O:利用标准库缓存加速文件访问,标准库内部再通过系统调度访问文件。

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

比如很多程序遇到换行时才真正输出,而换行前的内容,其实就是被标准库暂时缓存。

如何监控?

使用 df 查看文件系统磁盘空间、索引节点使用情况:

1
2
3
4
5
6
7
df -h /dev/sda1 
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda1 29G 3.1G 26G 11% /

df -i /dev/sda1
# Filesystem Inodes IUsed IFree IUse% Mounted on
# /dev/sda1 3870720 157460 3713260 5% /

通过 /proc/meminfo 查看缓存大小:

1
2
3
4
cat /proc/meminfo | grep -E "SReclaimable|Cached" 
# Cached: 748316 kB
# SwapCached: 0 kB
# SReclaimable: 179508 kB

内核使用 Slab 机制管理目录项和索引节点的缓存。/proc/meminfo 只给出了 Slab 的整体大小,查看 /proc/slabinfo 可查看 Slab 缓存具体情况(所有目录项和各种文件系统索引节点的缓存):

1
2
3
4
5
6
7
8
9
10
11
12
cat /proc/slabinfo | grep -E '^#|dentry|inode' 
# # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
# xfs_inode 0 0 960 17 4 : tunables 0 0 0 : slabdata 0 0 0
# ...
# ext4_inode_cache 32104 34590 1088 15 4 : tunables 0 0 0 : slabdata 2306 2306 0hugetlbfs_inode_cache 13 13 624 13 2 : tunables 0 0 0 : slabdata 1 1 0
# sock_inode_cache 1190 1242 704 23 4 : tunables 0 0 0 : slabdata 54 54 0
# shmem_inode_cache 1622 2139 712 23 4 : tunables 0 0 0 : slabdata 93 93 0
# proc_inode_cache 3560 4080 680 12 2 : tunables 0 0 0 : slabdata 340 340 0
# inode_cache 25172 25818 608 13 2 : tunables 0 0 0 : slabdata 1986 1986 0
# dentry 76050 121296 192 21 1 : tunables 0 0 0 : slabdata 5776 5776 0

# dentry 行表示目录项缓存,inode_cache 行表示 VFS 索引节点缓存,其余的则是各种文件系统的索引节点缓存。

使用 slabtop 查看目录项和索引节点占用的 Slab 缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
slabtop 
# Active / Total Objects (% used) : 277970 / 358914 (77.4%)
# Active / Total Slabs (% used) : 12414 / 12414 (100.0%)
# Active / Total Caches (% used) : 83 / 135 (61.5%)
# Active / Total Size (% used) : 57816.88K / 73307.70K (78.9%)
# Minimum / Average / Maximum Object : 0.01K / 0.20K / 22.88K
#
# OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME
# 69804 23094 0% 0.19K 3324 21 13296K dentry
# 16380 15854 0% 0.59K 1260 13 10080K inode_cache
# 58260 55397 0% 0.13K 1942 30 7768K kernfs_node_cache
# 485 413 0% 5.69K 97 5 3104K task_struct
# 1472 1397 0% 2.00K 92 16 2944K kmalloc-2048

# 按下 c 按照缓存大小排序,按下 a 按照活跃对象数排序。

磁盘 I/O

磁盘是可以持久化存储的设备,根据存储介质分类:

  • 机械磁盘(Hard Disk Driver,HDD):最小读写单位为扇区(一般 512Bytes)。主要由盘片和读写磁头组成,数据就存储在盘片的环状磁道中。在读写数据前需要移动读写磁头,定位到数据所在的磁道才能访问数据。如果 I/O 请求刚好连续则不需要磁道寻址,且可以通过预读的方式减少 I/O 次数获得最佳性能。与之相对随机 I/O 需要更多的磁头寻道和盘片旋转,读写速度较慢。

  • 固态磁盘 (Solid State Disk,SSD):最小读写单位为页(一般 4KB、8KB)。由固态电子元器件组成,不需要磁道寻址,连续 I/O 和随机 I/O 的性能都比机械磁盘好。但同样存在“先擦除再写入”的限制,随机读写导致大量的垃圾回收,所以随机 I/O 的性能比起连续 I/O 来差很多。

根据接口来分类: 不同的接口往往分配不同的设备名称。IDE(Integrated Drive Electronics,前缀 hd)、SCSI(Small Computer System Interface,前缀 sd) 、SAS(Serial Attached SCSI,前缀 sd) 、SATA(Serial ATA,前缀 sd) 、FC(Fibre Channel) 等。多块同类型的磁盘会按照 a、b、c 等的字母顺序来编号。

根据架构分类:把磁盘接入服务器后,按照不同的使用方式可以把它们划分为多种不同的架构。

  • 直接作为独立磁盘设备来使用,在磁盘上划分不同的逻辑分区(/dev/sda1)。

  • 把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列(Redundant Array of Independent Disks,RAID),以提高读写性能或提供冗余。

  • 把磁盘组合成网络存储集群,再通过 NFS、SMB、iSCSI 等网络存储协议暴露给服务器使用。

在 Linux 中磁盘以块为单位读写数据(块设备),并支持随机读写。每个块设备在驱动程序中被赋予主设备号(用来区分设备类型)和次设备号(给多个同类设备编号)。

通用块层

即在文件系统和磁盘驱动中间的一个块设备抽象层。

  • 与虚拟文件系统的功能类似。向上为文件系统和应用程序提供访问块设备的标准接口;向下把各种异构的磁盘设备抽象为统一的块设备,并提供统一框架来管理这些设备的驱动程序。

  • I/O 调度:给文件系统和应用程序发来的 I/O 请求排队,并通过重新排序、请求合并等方式提高磁盘读写的效率。

I/O 调度算法:

  • NONE:对文件系统和应用程序的 I/O 其实不做任何处理,常用在虚拟机中(此时磁盘 I/O 调度完全由物理机负责)。

  • NOOP:一个先入先出的队列,只做一些最基本的请求合并,常用于 SSD 磁盘。

  • CFQ:完全公平调度器(Completely Fair Scheduler),通常是默认的 I/O 调度器。为每个进程维护了一个 I/O 调度队列,按照时间片均匀分布每个进程的 I/O 请求;另外还支持进程 I/O 的优先级调度,适用于运行大量进程的系统。

  • DeadLine:分别为读、写请求创建 I/O 队列,提高机械磁盘的吞吐量,并确保达到最终期限的请求被优先处理。多用在 I/O 压力比较重的场景,比如数据库等。

I/O 栈

存储系统 I/O 的工作原理:

  • 文件系统层:虚拟文件系统和物理文件系统。为上层应用程序提供标准的文件访问接口;对下通过通用块层来存储和管理磁盘数据。

  • 通用块层:包括块设备 I/O 队列和 I/O 调度器。会对文件系统的 I/O 请求进行排队,再通过重新排序和请求合并,然后才要发送给下一级的设备层。

  • 设备层:包括存储设备和相应的驱动程序,负责最终物理设备的 I/O 操作。

存储系统的 I/O 是整个系统中最慢的部分,Linux 通过多种缓存机制来优化 I/O 效率:

  • 优化文件访问:使用页缓存、索引节点缓存、目录项缓存等多种缓存机制减少对下层块设备的直接调用。

  • 优化块设备访问:使用缓冲区来缓存块设备的数据。

img

磁盘性能指标

衡量磁盘性能需要考虑:

  • 使用率:磁盘处理 I/O 的时间百分比。过高的使用率(80%+)通常意味着磁盘 I/O 存在性能瓶颈。只考虑是否有 I/O,不考虑 I/O 大小。

  • 饱和度:磁盘处理 I/O 的繁忙程度。过高的饱和度意味着磁盘存在严重的性能瓶颈(100% 时磁盘无法接受新的 I/O 请求),大量顺序读写场景下更能反映系统性能(比如多媒体)。

  • IOPS(Input/Output Per Second):每秒的 I/O 请求数,大量随机读写场景下更能反映系统性能(比如数据库、大量小文件)。

  • 吞吐量:每秒的 I/O 请求大小。

  • 响应时间:I/O 请求从发出到收到响应的间隔时间。

以上指标可使用工具 fio 结合具体应用场景(顺序读/写,随机读/写)进行基准测试来评估。

饱和度通常没有简单的观测方法,但可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,与基准测试的结果(比如通过 fio)进行对比,综合评估磁盘的饱和情况。

如何监控?

使用 iostat 命令查看每个磁盘的使用率、IOPS、吞吐量等(数据源于 /proc/diskstats):

1
2
3
4
5
6
7
# -d -x 表示显示所有磁盘 I/O 的指标
iostat -d -x 1
# Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
# loop0 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
# loop1 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
# sda 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
# sdb 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
img

使用 pidstat 观察进程 I/O 情况:

1
2
3
pidstat -d 1 
# 13:39:51 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
# 13:39:52 102 916 0.00 4.00 0.00 0 rsyslogd

其每列的含义:

  • 用户 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 大小对进程进行排序:

1
2
3
4
5
6
7
8
iotop
# 进程的磁盘读写大小总数和磁盘真实的读写大小总数。因为缓存、缓冲区、I/O 合并等因素的影响,可能并不相等。
# Total DISK READ : 0.00 B/s | Total DISK WRITE : 7.85 K/s
# Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s

# 线程 ID、I/O 优先级、每秒读磁盘的大小、每秒写磁盘的大小、换入和等待 I/O 的时钟百分比等。
# TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
# 15055 be/3 root 0.00 B/s 7.85 K/s 0.00 % 0.00 % systemd-journald

优化总结

应用优化

大文件异步 I/O 和直接 I/O,小文件零拷贝(文件大小的阈值可灵活配置,参考 Nginx 的 directio 指令)。

文件系统优化

  • 根据实际负载场景选择最适合的文件系统。比如相比于 ext4(Ubuntu 默认),xfs(CentOS 默认)支持更大的磁盘分区(>16TB)和更大的文件数量,但缺点在于无法收缩,而 ext4 则可以。

  • 优化文件系统的配置选项。包括文件系统的特性(如 ext_attr、dir_index)、日志模式(如 journal、ordered、writeback)、挂载选项(如 noatime)等。比如使用 tune2fs 可以调整文件系统的特性(也可以查看文件系统超级块的内容)。 而通过 /etc/fstabmount 命令行参数,可以调整文件系统的日志模式和挂载选项等。

  • 优化文件系统的缓存。比如可以优化 pdflush 脏页的刷新频率(设置 dirty_expire_centisecs 和 dirty_writeback_centisecs)以及脏页的限额(调整 dirty_background_ratio 和 dirty_ratio 等)。还可以优化内核回收目录项缓存和索引节点缓存的倾向,即调整 vfs_cache_pressure(/proc/sys/vm/vfs_cache_pressure,默认值 100),数值越大表示越容易回收。

  • 使用内存文件系统 tmpfs 以获得更好的 I/O 性能 。tmpfs 把数据直接保存在内存中,而不是磁盘中。比如 /dev/shm 就是大多数 Linux 默认配置的一个内存文件系统,大小默认为总内存的一半,在不需要持久化时建议使用。

磁盘优化

  • 换用性能更好的磁盘,比如用 SSD 替代 HDD。

  • 使用 RAID 把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列。可以提高数据的可靠性和数据的访问性能。

  • 针对磁盘和应用程序 I/O 模式的特征,选择最适合的 I/O 调度算法。比如 SSD 和虚拟机中的磁盘通常用 noop 调度算法。而数据库应用更推荐使用 deadline 算法。

  • 对应用程序的数据进行磁盘级别的隔离。比如可以为日志、数据库等 I/O 压力比较重的应用,配置单独的磁盘。

  • 在顺序读比较多的场景中可以增大磁盘的预读数据,比如调整 /dev/sdb 的预读大小:

    • 调整选项 /sys/block/sdb/queue/read_ahead_kb,默认大小是 128 KB,单位为 KB。

    • 使用 blockdev 工具设置,比如 blockdev --setra 8192 /dev/sdb,单位是 512B(0.5KB),其数值总是 read_ahead_kb 两倍。

  • 优化内核块设备 I/O 的选项。比如可以适当增大磁盘队列长度 /sys/block/sdb/queue/nr_requests,以提升磁盘的吞吐量(也会导致 I/O 延迟增大)。

  • 发现性能急剧下降时还需要确认磁盘本身是否出现硬件错误:使用 dmesg 查看是否有硬件 I/O 故障的日志;使用 badblocks、``smartctl等工具检测磁盘硬件问题,或用 e2fsck 等来检测文件系统的错误;如果发现问题可以使用fsck` 等工具来修复。

img img

img

参考

分析案例可参考:

CATALOG
  1. 1. Linux I/O 原理与分析
    1. 1.1. 文件系统与磁盘
    2. 1.2. 文件系统
      1. 1.2.1. 索引节点和目录项
      2. 1.2.2. 虚拟文件系统(VFS)
      3. 1.2.3. Page Cache
      4. 1.2.4. 文件系统 I/O
        1. 1.2.4.1. 直接与非直接 I/O
        2. 1.2.4.2. 阻塞与非阻塞 I/O
        3. 1.2.4.3. 同步与异步 I/O
        4. 1.2.4.4. 缓冲与非缓冲 I/O
      5. 1.2.5. 如何监控?
    3. 1.3. 磁盘 I/O
      1. 1.3.1. 通用块层
      2. 1.3.2. I/O 栈
    4. 1.4. 磁盘性能指标
      1. 1.4.1. 如何监控?
    5. 1.5. 优化总结
      1. 1.5.1. 应用优化
      2. 1.5.2. 文件系统优化
      3. 1.5.3. 磁盘优化
    6. 1.6. 参考