建站不啰嗦,上手跟我做(十五)查看磁盘 IO

                            各种IO监视工具在Linux IO 体系结构中的位置 

                            源自 Linux Performance and Tuning Guidelines.pdf

1 系统级 IO 监控

iostat

  iostat -xdm 1 # 个人习惯

显示列名含义
%util代表磁盘繁忙程度。100% 表示磁盘繁忙, 0% 表示磁盘空闲。但是注意, 磁盘繁忙不代表磁盘 (带宽) 利用率高
argrq-sz提交给驱动层的 IO 请求大小, 一般不小于 4K, 不大于 max(readahead_kb, max_sectors_kb),可用于判断当前的 IO 模式, 一般情况下, 尤其是磁盘繁忙时, 越大代表顺序, 越小代表随机
svctm一次 IO 请求的服务时间, 对于单块盘, 完全随机读时, 基本在 7ms 左右, 既寻道 + 旋转延迟时间
注: 各统计量之间关系
表达式含义
%util = (r/s + w/s) * svctm / 1000# 队列长度 = 到达率 * 平均服务时间
avgrq-sz = (rMB/s + wMB/s) * 2048 / (r/s + w/s)# 2048 为 1M / 512

总结:

iostat 统计的是通用块层经过合并 (rrqm/s, wrqm/s) 后, 直接向设备提交的 IO 数据, 可以反映系统整体的 IO 状况, 但是有以下 2 个缺点:

1 距离业务层比较遥远, 跟代码中的 write,read 不对应 (由于系统预读 + pagecache + IO 调度算法等因素, 也很难对应)

2 是系统级, 没办法精确到进程, 比如只能告诉你现在磁盘很忙, 但是没办法告诉你是谁在忙, 在忙什么?

[root@srm-mongodb01 ~]# yum install sysstat
Loaded plugins: product-id, search-disabled-repos
Resolving Dependencies

2 进程级 IO 监控

iotop 和 pidstat (仅 rhel6u 系列)

iotop 顾名思义, io 版的 top

pidstat 顾名思义, 统计进程 (pid) 的 stat, 进程的 stat 自然包括进程的 IO 状况

这两个命令, 都可以按进程统计 IO 状况, 因此可以回答你以下二个问题

  1. 当前系统哪些进程在占用 IO, 百分比是多少?

  2. 占用 IO 的进程是在读? 还是在写? 读写量是多少?
    pidstat 参数很多, 仅给出几个个人习惯

    pidstat -d 1 #只显示 IO

   pidstat -u -r -d -t 1        # -d IO 信息,

                                       # -r 缺页及内存信息
                                       # -u CPU使用率
                                       # -t 以线程为统计单位
                                       # 1  1秒统计一次

iotop, 很简单, 直接敲命令

block_dump, iodump

iotop 和 pidstat 用着很爽, 但两者都依赖于 /proc/pid/io 文件导出的统计信息, 这个对于老一些的内核是没有的, 比如 rhel5u2

因此只好用以上 2 个穷人版命令来替代:

echo 1 > /proc/sys/vm/block_dump # 开启 block_dump, 此时会把 io 信息输入到 dmesg 中

                                                    # 源码: submit_bio@ll_rw_blk.c:3213

watch -n 1 "dmesg -c | grep -oP"\w+(\d+): (WRITE|READ)"| sort | uniq -c"

                                                     # 不停的dmesg -c

echo 0 > /proc/sys/vm/block_dump # 不用时关闭

iotop.stp

systemtap 脚本, 一看就知道是 iotop 命令的穷人复制版, 需要安装 Systemtap, 默认每隔 5 秒输出一次信息

stap iotop.stp # examples/io/iotop.stp

总结

进程级 IO 监控 ,

  1. 可以回答系统级 IO 监控不能回答的 2 个问题
  2. 距离业务层相对较近 (例如, 可以统计进程的读写量)

但是也没有办法跟业务层的 read,write 联系在一起, 同时颗粒度较粗, 没有办法告诉你, 当前进程读写了哪些文件? 耗时? 大小 ?

3 业务级 IO 监控

ioprofile

ioprofile 可以回答你以下三个问题:

1  当前进程某时间内,在业务层面读写了哪些文件(read, write)?

2  读写次数是多少?(read, write的调用次数)

3  读写数据量多少?(read, write的byte数)

假设某个行为会触发程序一次IO动作,例如: "一个页面点击,导致后台读取A,B,C文件"

============================================

./io_event   # 假设模拟一次IO行为,读取A文件一次, B文件500次, C文件500次

ioprofile  -p  `pidof  io_event` -c count   # 读写次数

ioprofile  -p  `pidof  io_event` -c times   # 读写耗时

ioprofile  -p  `pidof  io_event` -c sizes    # 读写大小

注: ioprofile 仅支持多线程程序,对单线程程序不支持. 对于单线程程序的IO业务级分析,strace足以。

总结:

    ioprofile本质上是strace,因此可以看到read,write的调用轨迹,可以做业务层的io分析(mmap方式无能为力)

4 文件级 IO 监控

   文件级IO监控可以配合/补充"业务级和进程级"IO分析

   文件级IO分析,主要针对单个文件, 回答当前哪些进程正在对某个文件进行读写操作.

   1 lsof   或者  ls /proc/pid/fd

   2 inodewatch.stp

lsof 告诉你 当前文件由哪些进程打开

lsof ../io # io 目录 当前由 bash 和 lsof 两个进程打开

lsof 命令 只能回答静态的信息, 并且 "打开" 并不一定 "读取", 对于 cat ,echo 这样的命令, 打开和读取都是瞬间的,lsof 很难捕捉

可以用 inodewatch.stp 来弥补

stap inodewatch.stp major minor inode # 主设备号, 辅设备号, 文件 inode 节点号

stap inodewatch.stp 0xfd 0x00 523170 # 主设备号, 辅设备号, inode 号, 可以通过 stat 命令获得

5 IO 模拟器

iotest.py # 见附录

开发人员可以 利用 ioprofile (或者 strace) 做详细分析系统的 IO 路径, 然后在程序层面做相应的优化。

但是一般情况下调整程序, 代价比较大, 尤其是当不确定修改方案到底能不能有效时, 最好有某种模拟途径以快速验证。

以为我们的业务为例,发现某次查询时, 系统的 IO 访问模式如下:

访问了 A 文件一次

访问了 B 文件 500 次, 每次 16 字节, 平均间隔 502K

访问了 C 文件 500 次, 每次 200 字节, 平均间隔 4M

这里 B,C 文件是交错访问的, 既

1 先访问 B, 读 16 字节,

2 再访问 C, 读 200 字节,

3 回到 B, 跳 502K 后再读 16 字节,

4 回到 C, 跳 4M 后, 再读 200 字节

5 重复 500 次

strace 文件如下:

一个简单朴素的想法, 将 B,C 交错读, 改成先批量读 B , 再批量读 C, 因此调整 strace 文件如下:

将调整后的 strace 文件, 作为输入交给 iotest.py, iotest.py 按照 strace 文件中的访问模式, 模拟相应的 IO

iotest.py -s io.strace -f fmap

fmap 为映射文件, 将 strace 中的 222,333 等 fd, 映射到实际的文件中

111 = /opt/work/io/A.data
222 = /opt/work/io/B.data
333 = /opt/work/io/C.data

6 磁盘碎片整理

一句话: 只要磁盘容量不常年保持 80% 以上, 基本上不用担心碎片问题。

如果实在担心, 可以用 defrag 脚本

7 其他 IO 相关命令

blockdev 系列

命令含义
blockdev --getbsz /dev/sdc1# 查看 sdc1 盘的块大小
block blockdev --getra /dev/sdc1# 查看 sdc1 盘的预读 (readahead_kb) 大小
blockdev --setra 256 /dev/sdc1# 设置 sdc1 盘的预读 (readahead_kb) 大小, 低版的内核通过 /sys 设置, 有时会失败, 不如 blockdev 靠谱

上一篇 建站不啰嗦,上手跟我做(十四)查看内存使用情况
目录
下一篇 建站不啰嗦,上手跟我做(十六)查看 cpu 占用