8000 添加文件性能优化和磁盘性能优化 · andrewbytecoder/linux-note@6640219 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6640219

Browse files
author
wangyazhou
committed
添加文件性能优化和磁盘性能优化
1 parent 25a7057 commit 6640219

File tree

5 files changed

+102
-32
lines changed

5 files changed

+102
-32
lines changed
Loading
Loading
Loading
Loading

linux/linux-performance.md

Lines changed: 102 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,9 @@ dd读取文件,首次慢,但是第二次快,可以通过以上命令查看
788788
789789
790790
## 文件系统
791+
792+
![[Pasted image 20250630092025.png]]
793+
791794
磁盘为系统提供了最基本的持久化存储。
792795
文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构
793796
### 文件系统原理
@@ -843,6 +846,18 @@ VFS 定义了一组所有文件系统都支持的数据结构和标准接口。
843846
#### 文件系统缓存
844847
#### 文件系统种类
845848
### 性能指标
849+
850+
#### 从文件系统和磁盘 I/O 的性能指标出发
851+
852+
![[Pasted image 20250630092707.png]]
853+
854+
- lsblk
855+
856+
857+
#### 从工具出发
858+
859+
![[Pasted image 20250630092729.png]]
860+
846861
#### 容量
847862
```bash
848863
# -h 人类可读形式展示
@@ -852,20 +867,84 @@ df -i /dev/sfa1
852867
```
853868
索引节点的容量,(也就是 Inode 个数)是在格式化磁盘时设定好的,一般由格式化工具自动生成。当你发现索引节点空间不足,但磁盘空间充足时,很可能就是过多小文件导致的。
854869
所以,一般来说,删除这些小文件,或者把它们移动到索引节点充足的其他磁盘中,就可以解决这个问题。
855-
#### IOPS
856-
#### 缓存命中率
870+
871+
#### 阻塞、非阻塞I/O与同步、异步I/O的区别和联系
872+
首先我们来看阻塞和非阻塞 I/O。根据应用程序是否阻塞自身运行,可以把 I/O 分为阻塞I/O 和非阻塞 I/O。
873+
- 所谓阻塞 I/O,是指应用程序在执行 I/O 操作后,如果没有获得响应,就会阻塞当前线程,不能执行其他任务
874+
- 所谓非阻塞 I/O,是指应用程序在执行 I/O 操作后,不会阻塞当前的线程,可以继续执行其他的任务。
875+
876+
再来看同步 I/O 和异步 I/O。根据 I/O 响应的通知方式的不同,可以把文件 I/O 分为同步I/O 和异步 I/O。
877+
- 所谓同步 I/O,是指收到 I/O 请求后,系统不会立刻响应应用程序;等到处理完成,系统才会通过系统调用的方式,告诉应用程序 I/O 结果。
878+
- 所谓异步 I/O,是指收到 I/O 请求后,系统会先告诉应用程序 I/O 请求已经收到,随后再去异步处理;等处理完成后,系统再通过事件通知的方式,告诉应用程序结果
879+
857880
### 性能剖析
858-
#### df
859-
#### strace
860-
#### vmstat
861-
#### sar
862-
#### perf
863-
#### proc文件系统
881+
文件系统中的文件最终是要进行落盘的,最容易想到的是存储空间的使用情况,包括容量、使用量以及剩余空间等。还有一个容易忽略的是索引节点的使用情况,它也包括容量、使用量以及剩余量等三个指标。如果文件系统中存储过多的小文件,就可能碰到
882+
索引节点容量已满的问题。
883+
884+
其次,你应该想到的是前面多次提到过的缓存使用情况,包括页缓存、目录项缓存、索引节点缓存以及各个具体文件系统(如 ext4、XFS 等)的缓存。这些缓存会使用速度更快的内存,用来临时存储文件数据或者文件系统的元数据,从而可以减少访问慢速磁盘的次数。
885+
886+
除了以上这两点,文件 I/O 也是很重要的性能指标,包括 IOPS(包括 r/s 和 w/s)、响应时间(延迟)以及吞吐量(B/s)等。在考察这类指标时,通常还要考虑实际文件的读写情况。比如,结合文件大小、文件数量、I/O 类型等,综合分析文件 I/O 的性能。
887+
888+
```bash
889+
# 随机读
890+
fio -name=randread -direct=1 -iodepth=64 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb 
891+
# 随机写
892+
fio -name=randwrite -direct=1 -iodepth=64 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb 
893+
# 顺序读
894+
fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb 
895+
# 顺序写
896+
fio -name=write -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb 
897+
```
898+
direct,表示是否跳过系统缓存。上面示例中,我设置的 1 ,就表示跳过系统缓存。
899+
900+
iodepth,表示使用异步 I/O(asynchronous I/O,简称 AIO)时,同时发出的 I/O 请求上限。在上面的示例中,我设置的是 64。
901+
902+
rw,表示 I/O 模式。我的示例中, read/write 分别表示顺序读 / 写,而 randread/randwrite 则分别表示随机读 / 写。
903+
904+
ioengine,表示 I/O 引擎,它支持同步(sync)、异步(libaio)、内存映射(mmap)、网络(net)等各种 I/O 引擎。上面示例中,我设置的 libaio 表示使用异步 I/O。
905+
906+
bs,表示 I/O 的大小。示例中,我设置成了 4K(这也是默认值)。
907+
908+
filename,表示文件路径,当然,它可以是磁盘路径(测试磁盘性能),也可以是文件路径(测试文件系统性能)。示例中,我把它设置成了磁盘 /dev/sdb。不过注意,用磁盘路径测试写,会破坏这个磁盘中的文件系统,所以在使用前,你一定要事先做好数据备份。
909+
910+
864911
### 调优方法
865-
#### 文件系统选型
866-
#### 利用文件系统缓存
867-
#### I/O隔离
868912
913+
![[Pasted image 20250630093928.png]]
914+
915+
第一,在文件系统的原理中,我介绍了查看文件系统容量的工具 df。它既可以查看文件系统数据的空间容量,也可以查看索引节点的容量。至于文件系统缓存,我们通过/proc/meminfo、/proc/slabinfo 以及 slabtop 等各种来源,观察页缓存、目录项缓存、索引节点缓存以及具体文件系统的缓存情况
916+
917+
第二,在磁盘 I/O 的原理中,我们分别用 iostat 和 pidstat 观察了磁盘和进程的 I/O 情况。它们都是最常用的 I/O 性能分析工具。通过 iostat ,我们可以得到磁盘的 I/O 使用率、吞吐量、响应时间以及 IOPS 等性能指标;而通过 pidstat ,则可以观察到进程的 I/O吞吐量以及块设备 I/O 的延迟等。
918+
919+
第三,在狂打日志的案例中,我们先用 top 查看系统的 CPU 使用情况,发现 iowait 比较高;然后,又用 iostat 发现了磁盘的 I/O 使用率瓶颈,并用 pidstat 找出了大量 I/O 的进程;最后,通过 strace 和 lsof,我们找出了问题进程正在读写的文件,并最终锁定性能问题的来源——原来是进程在狂打日志。
920+
921+
第四,在磁盘 I/O 延迟的单词热度案例中,我们同样先用 top、iostat ,发现磁盘有 I/O瓶颈,并用 pidstat 找出了大量 I/O 的进程。可接下来,想要照搬上次操作的我们失败了。在随后的 strace 命令中,我们居然没看到 write 系统调用。于是,我们换了一个思路,用新工具 filetop 和 opensnoop ,从内核中跟踪系统调用,最终找出瓶颈的来源。
922+
923+
最后,同样的思路,我们先用 top、iostat 以及 pidstat ,确定并找出 I/O 性能问题的瓶颈来源,它们正是 mysqld 和 redis-server。随后,我们又用 strace+lsof 找出了它们正在读写的文件。
924+
925+
#### 应用程序优化
926+
应用程序处于整个 I/O 栈的最上端,它可以通过系统调用,来调整 I/O 模式(如顺序还是随机、同步还是异步), 同时,它也是 I/O 数据的最终来源。在我看来,可以有这么几种方式来优化应用程序的 I/O 性能。
927+
928+
第一,可以用追加写代替随机写,减少寻址开销,加快 I/O 写的速度。
929+
第二,可以借助缓存 I/O ,充分利用系统缓存,降低实际 I/O 的次数。
930+
第三,可以在应用程序内部构建自己的缓存,或者用 Redis 这类外部缓存系统。这样,一方面,能在应用程序内部,控制缓存的数据和生命周期;另一方面,也能降低其他应用程序使用缓存对自身的影
931+
932+
C 标准库提供的 fopen、fread 等库函数,都会利用标准库的缓存,减少磁盘的操作。而你直接使用 open、read 等系统调用时,就只能利用操作系统提供的页缓存和缓冲区等,而没有库函数的缓存可用。
933+
934+
第四,在需要频繁读写同一块磁盘空间时,可以用 mmap 代替 read/write,减少内存的拷贝次数。
935+
第五,在需要同步写的场景中,尽量将写请求合并,而不是让每个请求都同步写入磁盘,即可以用 fsync() 取代 O_SYNC。
936+
第六,在多个应用程序共享相同磁盘时,为了保证 I/O 不被某个应用完全占用,推荐你使用 cgroups 的 I/O 子系统,来限制进程 / 进程组的 IOPS 以及吞吐量。
937+
最后,在使用 CFQ 调度器时,可以用 ionice 来调整进程的 I/O 调度优先级,特别是提高核心应用的 I/O 优先级。ionice 支持三个优先级类:Idle、Best-effort 和 Realtime。其中, Best-effort 和 Realtime 还分别支持 0-7 的级别,数值越小,则表示优先级别越高。
938+
939+
#### 文件系统优化
940+
941+
应用程序访问普通文件时,实际是由文件系统间接负责,文件在磁盘中的读写。所以,跟文件系统中相关的也有很多优化 I/O 性能的方式。
942+
943+
第一,你可以根据实际负载场景的不同,选择最适合的文件系统。比如 Ubuntu 默认使用ext4 文件系统,而 CentOS 7 默认使用 xfs 文件系统。
944+
第二,在选好文件系统后,还可以进一步优化文件系统的配置选项,包括文件系统的特性(如 ext_attr、dir_index)、日志模式(如 journal、ordered、writeback)、挂载选项(如 noatime)等等。
945+
第三,可以优化文件系统的缓存
946+
比如,你可以优化 pdflush 脏页的刷新频率(比如设置 dirty_expire_centisecs 和dirty_writeback_centisecs)以及脏页的限额(比如调整 dirty_background_ratio 和dirty_ratio 等)。
947+
最后,在不需要持久化时,你还可以用内存文件系统 tmpfs,以获得更好的 I/O 性能 。tmpfs 把数据直接保存在内存中,而不是磁盘中。比如 /dev/shm/ ,就是大多数 Linux 默认配置的一个内存文件系统,它的大小默认为总内存的一半。
869948
870949
## 磁盘I/O
871950
磁盘是可以持久化存储的设备,根据存储介质的不同,常见磁盘可以分为两类:机械磁盘(Hard Disk Driver)和固态磁盘(Solid State Disk)。
@@ -902,9 +981,6 @@ df -i /dev/sfa1
902981
903982
通用块层是 Linux 磁盘 I/O 的核心。向上,它为文件系统和应用程序,提供访问了块设备的标准接口;向下,把各种异构的磁盘设备,抽象为统一的块设备,并会对文件系统和应用程序发来的 I/O 请求,进行重新排序、请求合并等,提高了磁盘访问的效率。
904983
905-
#### 磁盘管理
906-
#### 磁盘类型
907-
#### 磁盘接口
908984
#### 磁盘I/O栈
909985
我们可以把 Linux 存储系统的 I/O 栈,由上到下分为三个层次,分别是文件系统层、通用块层和设备层。这三个 I/O 层的关系如下图所示,这其实也是 Linux 存储系统的 I/O 栈全景图。
910986
![[Pasted image 20250627182100.png]]
@@ -986,28 +1062,22 @@ TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO> 
9861062
```
9871063
9881064
989-
#### 使用率
990-
#### IOPS
991-
#### 吞吐量
992-
#### IOWAIT
9931065
### 性能剖析
994-
#### dstat
995-
#### sar
996-
#### iostat
997-
#### pidstat
998-
#### iotop
999-
#### iolatency
1000-
#### blktrace
1001-
#### fio
1002-
#### perf
10031066
10041067
### 调优方法
1005-
#### 系统调用
1006-
#### I/O资源控制
1007-
#### 充分利用缓存
1008-
#### RAID
1009-
#### I/O隔离
10101068
1069+
#### 磁盘优化
1070+
数据的持久化存储,最终还是要落到具体的物理磁盘中,同时,磁盘也是整个 I/O 栈的最底层。
1071+
第一,最简单有效的优化方法,就是换用性能更好的磁盘,比如用 SSD 替代 HDD。
1072+
第二,我们可以使用 RAID ,把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列。这样做既可以提高数据的可靠性,又可以提升数据的访问性能。
1073+
第三,针对磁盘和应用程序 I/O 模式的特征,我们可以选择最适合的 I/O 调度算法。比方说,SSD 和虚拟机中的磁盘,通常用的是 noop 调度算法。而数据库应用,我更推荐使用deadline 算法。
1074+
第四,我们可以对应用程序的数据,进行磁盘级别的隔离。比如,我们可以为日志、数据库等 I/O 压力比较重的应用,配置单独的磁盘。
1075+
1076+
第五,在顺序读比较多的场景中,我们可以增大磁盘的预读数据,比如,你可以通过下面两种方法,调整 /dev/sdb 的预读大小。
1077+
- 调整内核选项 /sys/block/sdb/queue/read_ahead_kb,默认大小是 128 KB,单位为KB。
1078+
- 使用 blockdev 工具设置,比如 blockdev --setra 8192 /dev/sdb,注意这里的单位是512B(0.5KB),所以它的数值总是 read_ahead_kb 的两倍。
1079+
第六,我们可以优化内核块设备 I/O 的选项。比如,可以调整磁盘队列的长度/sys/block/sdb/queue/nr_requests,适当增大队列长度,可以提升磁盘的吞吐量(当然也会导致 I/O 延迟增大)
1080+
最后,要注意,磁盘本身出现硬件错误,也会导致 I/O 性能急剧下降,所以发现磁盘性能急剧下降时,你还需要确认,磁盘本身是不是出现了硬件错误。
10111081
10121082
## 网络
10131083
### 网络原理

0 commit comments

Comments
 (0)
0