本文通过学习蓝胖子系列课程总结而来。
作为开发对磁盘 IO 的疑问
1、如何发现和衡量磁盘的性能问题?
2、磁盘 IO 延迟,应该怎么办?
下面介绍我对这里问题的思考以及针对 IO 性能问题的排查思路
概念模型
了解文件系统的概念模型,建立起从应用程序读写到硬盘问题读写的全路径蓝图
往磁盘上写数据有 3 种方式:
1)经过文件系统,文件系统写到 page cache 上,再由一个专门的进程负责将 page cache 里的数据刷到磁盘上。
2)经过文件系统,但是不写到 page cache 上,直接往磁盘块设备上写,这种 io 被称为直接 io。直接 io 用于备份文件时使用,这样不会污染文件系统 cache,造成 cache 命中率下降。 3) 不经过文件系统,直接写到块设备上,这种 io 叫裸 io。
块设备对 io 请求的处理是典型的排队模型,这对于我们分析性能问题至关重要,可以用排队论去解释延迟现象。
如何衡量磁盘 IO 性能
从系统整体看 IO
vmstat
或者top
命令看 cache/buffer 的值
# vmstat
procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 0 0 1503252 216428 6503928
top
看 cpu wait 值大小,看 cache/buffer 的值
top - 19:29:39 up 13:10, 4 users, load average: 0.61, 0.55, 0.58
Tasks: 156 total, 1 running, 155 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.8 us, 1.6 sy, 0.0 ni, 97.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 32779456 total, 9243644 free, 20442492 used, 3093320 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 11862028 avail Mem
如果 cache buffer 的值长时间过低,说明系统缓存命中率大大降低,很可能导致系统延迟。
iostat
查看 io 使用率和饱和度
# 每隔一秒查询一次
iostat -x 1
使用扩展模式寻找繁忙磁盘(超过 60%使用率),较高的平均服务时间(超过大概 10ms),以及高 IOPS 可能存在 io 性能问题。
rrqm/s:每秒这个设备相关的读取请求有多少被Merge了(当系统调用需要读取数据的时候,VFS将请求发到各个FS,如果FS发现不同的读取请求读取的是相同Block的数据,FS会将这个请求合并Merge);wrqm/s:每秒这个设备相关的写入请求有多少被Merge了。
rsec/s:每秒读取的扇区数;
wsec/:每秒写入的扇区数。
rKB/s:The number of read requests that were issued to the device per second;
wKB/s:The number of write requests that were issued to the device per second;
avgrq-sz 平均请求扇区的大小
avgqu-sz 是平均请求队列的长度。毫无疑问,队列长度越短越好。
await:每一个IO请求的处理的平均时间(单位是微秒毫秒)。这里可以理解为IO的响应时间,一般地系统IO响应时间应该低于5ms,如果大于10ms就比较大了。
这个时间包括了队列时间和服务时间,也就是说,一般情况下,await大于svctm,它们的差值越小,则说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
svctm 表示平均每次设备I/O操作的服务时间(以毫秒为单位)。如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长, 系统上运行的应用程序将变慢。
%util:在统计时间内所有处理IO时间,除以总共统计时间。例如,如果统计间隔1秒,该设备有0.8秒在处理IO,而0.2秒闲置,那么该设备的%util = 0.8/1 = 80%,所以该参数暗示了设备的繁忙程度。一般地,如果该参数是100%表示设备已经接近满负荷运行了(当然如果是多磁盘,即使%util是100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)。
从进程看 IO
iotop
默认按 io 使用率从大到小排列,这样便能找出最消耗 io 的进程
Total DISK READ : 0.00 B/s | Total DISK WRITE : 0.00 B/s
Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % systemd --switch
从代码看 IO
找到了具体的进程后,最终还是得回归到具体代码段,找出问题代码。
以 golang 程序为例,go trace 工具可查看系统调用产生的延迟,如果从整体上看 io 达到了瓶颈,那么可以由这个工具来看系统在系统调用上消耗最多的地方来定位代码,以确定是否是此代码导致的 io 问题。