建站不啰嗦,上手跟我做(十五)查看磁盘 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 状况, 因此可以回答你以下二个问题
-
当前系统哪些进程在占用 IO, 百分比是多少?
-
占用 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 监控 ,
- 可以回答系统级 IO 监控不能回答的 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 靠谱 |