剖析生产系统的I/O模式

了解I/O的特点对于优化系统性能非常重要,I/O是顺序的还是随机的,是读操作还是写操作,读写的比例是多少,I/O数据块的大小,这些都是影响性能的关键因素。很多存储设备都基于特定的I/O模式做过调校,通用的测试工具跑分都相当漂亮,然而一到实际环境区别就来了,同样的应用环境下,不同的设备表现可能天差地别。我就见过不同厂商的设备,档次差不多,测试跑分高的那个在生产环境下的IO响应速度却慢了十倍。所以跑分高的设备真的不一定适合你的应用。

如果能够模拟出应用的I/O模式,那么在问题复现、乃至设备选型等方面都会有很大帮助。在此之前,了解I/O模式是第一步,这并不容易,像iostat之类的工具只能看到平均值,然而应用系统的I/O请求有可能是波浪式的,一秒之内也可以时高时低,I/O延迟可能平均值不高但是波动很大,而且I/O块大小也可以是变化的,尤其现在大数据应用的块大小可能在很大的范围内变化,与过去常见的交易型数据库有所不同,它们的块大小基本是固定的。

要剖析生产系统的I/O模式,好像没有现成的工具。但是我们可以利用blktrace自己做一个,blktrace在内核的block layer记录每一个I/O,提供了分析的素材。它记录的格式如下:

下面是一个简化版的示例,主要利用了Event “Q”和”C”,分别表示IO开始和IO完成,两者之间的耗时就相当于iostat看到的await,但blktrace可以精确到单个IO:

#!/bin/bash

if [ $# -ne 1 ]; then
echo "Usage: $0 <block_device_name>"
exit
fi
if [ ! -b $1 ]; then
echo "could not find block device $1"
exit
fi

duration=10
echo "running blktrace for $duration seconds to collect data..."
timeout $duration blktrace -d $1 >/dev/null 2>&1

DEVNAME=`basename $1`

echo "parsing blktrace data..."
blkparse -i $DEVNAME |sort -g -k8 -k10 -k4 |awk '
BEGIN {
total_read=0;
total_write=0;
maxwait_read=0;
maxwait_write=0;
}
{
if ($6=="Q") {
queue_ts=$4;
block=$8;
nblock=$10;
rw=$7;
};
if ($6=="C" && $8==block && $10==nblock && $7==rw) {
await=$4-queue_ts;
if (rw=="R") {
if (await>maxwait_read) maxwait_read=await;
total_read++;
read_count_block[nblock]++;
if (await>0.001) read_count1++;
if (await>0.01) read_count10++;
if (await>0.02) read_count20++;
if (await>0.03) read_count30++;
}
if (rw=="W") {
if (await>maxwait_write) maxwait_write=await;
total_write++;
write_count_block[nblock]++;
if (await>0.001) write_count1++;
if (await>0.01) write_count10++;
if (await>0.02) write_count20++;
if (await>0.03) write_count30++;
}
}
} END {
printf("========\nsummary:\n========\n");
printf("total number of reads: %d\n", total_read);
printf("total number of writes: %d\n", total_write);
printf("slowest read : %.6f second\n", maxwait_read);
printf("slowest write: %.6f second\n", maxwait_write);
printf("reads\n> 1ms: %d\n>10ms: %d\n>20ms: %d\n>30ms: %d\n", read_count1, read_count10, read_count20, read_count30);
printf("writes\n> 1ms: %d\n>10ms: %d\n>20ms: %d\n>30ms: %d\n", write_count1, write_count10, write_count20, write_count30);
printf("\nblock size:%16s\n","Read Count");
for (i in read_count_block)
printf("%10d:%16d\n", i, read_count_block[i]);
printf("\nblock size:%16s\n","Write Count");
for (i in write_count_block)
printf("%10d:%16d\n", i, write_count_block[i]);
}'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/bash
 
if [ $# -ne 1 ]; then
        echo "Usage: $0 <block_device_name>"
        exit
fi
if [ ! -b $1 ]; then
        echo "could not find block device $1"
        exit
fi
 
duration=10
echo "running blktrace for $duration seconds to collect data..."
timeout $duration blktrace -d $1 >/dev/null 2>&1
 
DEVNAME=`basename $1`
 
echo "parsing blktrace data..."
blkparse -i $DEVNAME |sort -g -k8 -k10 -k4 |awk '
BEGIN   {
        total_read=0;
        total_write=0;
        maxwait_read=0;
        maxwait_write=0;
}
{
        if ($6=="Q") {
                queue_ts=$4;
                block=$8;
                nblock=$10;
                rw=$7;
        };
        if ($6=="C" && $8==block && $10==nblock && $7==rw) {
                await=$4-queue_ts;
                if (rw=="R") {
                        if (await>maxwait_read) maxwait_read=await;
                        total_read++;
                        read_count_block[nblock]++;
                        if (await>0.001) read_count1++;
                        if (await>0.01) read_count10++;
                        if (await>0.02) read_count20++;
                        if (await>0.03) read_count30++;
                }
                if (rw=="W") {
                        if (await>maxwait_write) maxwait_write=await;
                        total_write++;
                        write_count_block[nblock]++;
                        if (await>0.001) write_count1++;
                        if (await>0.01) write_count10++;
                        if (await>0.02) write_count20++;
                        if (await>0.03) write_count30++;
                }
        }
} END   {
        printf("========\nsummary:\n========\n");
        printf("total number of reads: %d\n", total_read);
        printf("total number of writes: %d\n", total_write);
        printf("slowest read : %.6f second\n", maxwait_read);
        printf("slowest write: %.6f second\n", maxwait_write);
        printf("reads\n> 1ms: %d\n>10ms: %d\n>20ms: %d\n>30ms: %d\n", read_count1, read_count10, read_count20, read_count30);
        printf("writes\n> 1ms: %d\n>10ms: %d\n>20ms: %d\n>30ms: %d\n", write_count1, write_count10, write_count20, write_count30);
        printf("\nblock size:%16s\n","Read Count");
        for (i in read_count_block)
                printf("%10d:%16d\n", i, read_count_block[i]);
        printf("\nblock size:%16s\n","Write Count");
        for (i in write_count_block)
                printf("%10d:%16d\n", i, write_count_block[i]);
}'

输出示例:

========
summary:
========
total number of reads: 1081513
total number of writes: 0
slowest read : 0.032560 second
slowest write: 0.000000 second
reads
> 1ms: 18253
>10ms: 17058
>20ms: 17045
>30ms: 780
writes
> 1ms: 0
>10ms: 0
>20ms: 0
>30ms: 0

block size: Read Count
256: 93756
248: 1538
64: 98084
56: 7475
8: 101218
48: 15889
240: 1637
232: 1651
224: 1942
40: 21693
216: 1811
32: 197893
208: 1907
24: 37787
128: 97382
16: 399850

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
========
summary:
========
total number of reads: 1081513
total number of writes: 0
slowest read : 0.032560 second
slowest write: 0.000000 second
reads
> 1ms: 18253
>10ms: 17058
>20ms: 17045
>30ms: 780
writes
> 1ms: 0
>10ms: 0
>20ms: 0
>30ms: 0
 
block size:      Read Count
       256:           93756
       248:            1538
        64:           98084
        56:            7475
         8:          101218
        48:           15889
       240:            1637
       232:            1651
       224:            1942
        40:           21693
       216:            1811
        32:          197893
       208:            1907
        24:           37787
       128:           97382
        16:          399850

这个例子统计了IO的读/写数量、最大延迟、延迟的分布情况、块大小及数量,这些信息比iostat要具体得多,有助于进一步了解系统的IO模式。blktrace数据还有更多的利用空间等待你去发掘,譬如还可以根据时间戳去统计每个毫秒内的IO数,有助于更微观地了解IO请求数量的波动。

参考:
利用BLKTRACE分析IO性能

转载自:

http://linuxperf.com/?cat=11

剖析生产系统的I/O模式的更多相关文章

  1. 微博feed系统的推(push)模式和拉(pull)模式和时间分区拉模式架构探讨

    sns系统,微博系统都应用到了feed(每条微博或者sns里的新鲜事等我们称作feed)系统,不管是twitter.com或者国内的新浪微博,人人网等,在各种技术社区,技术大会上都在分享自己的feed ...

  2. 如何设置Win7系统中的上帝模式GodMode(转载)

    如何设置Win7系统中的上帝模式GodMode(转载) NT6系统中隐藏了一个秘密的“GodMode”,字面上译为“上帝模式”.God Mode其实就是一个简单的文件夹窗口,但包含了几乎所有系统的设置 ...

  3. CentOS7+Tomcat 生产系统部署

    1 准备OS账户 安全起见,本着最小权限原则,生产系统决不同意使用root账户来执行tomcat.为此,建立新账户tomcat,并设定登录password. useradd tomcat passwd ...

  4. [置顶] android系统如何在静音模式下关闭camera拍照声音(2)

    之前写过一篇“android系统如何在静音模式下关闭camera拍照声音”的博客,今天来写他的续篇,继续探讨这个问题. 公司新需求,要求在camera应用中添加一个开关,可以进行拍照声音的关闭和开启. ...

  5. runloop是iOS系统上的actor模式

    runloop是iOS系统上的actor模式(单线程派发的)

  6. CentOS修改系统的默认启动模式为命令号界面

    CentOS在安装桌面之后,将图形界面设置为默认模式,给PC内存带来较大压力,所以希望修改系统的默认启动模式为命令号界面,而不启动图形界面.方法如下: 1.以 root用户登录系统:或者 用su切换到 ...

  7. Atitit.biz业务系统 面向框架  面向模式---------数据映射imp

    Atitit.biz业务系统 面向框架  面向模式---------数据映射imp 1.1. 面向变量  面向过程  面向对象 面向组件  面向框架  面向服务 面向模式1 1.2. 第2章 架构模式 ...

  8. JVM性能分析 | 一次生产系统Full GC问题分析与排查总结

    一次生产系统Full GC问题分析与排查总结 背景 最近某线上业务系统生产环境频频CPU使用率过低,频繁告警,通过重启可以缓解,但是过了一段时间又会继续预警,线上两个服务节点相继出现CPU资源紧张,导 ...

  9. 生产系统CPU飙高问题排查

    现状 生产系统CPU占用过高,并且进行了报警 排查方法 执行top命令,查看是那个进程导致的,可以确定是pid为22168的java应用导致的 执行top -Hp命令,查看这个进程的那个线程导致cpu ...

随机推荐

  1. javascript加超链接

    JavaScript link 方法:给字符串加上超链接JavaScript link 方法link 方法返回使用 HTML a 标签属性定义的(斜体)字符串.其语法如下:str_object.lin ...

  2. WUSTOJ 1296: JAM计数法(Java)

    题目链接:

  3. nginx与PHP编译configure

    configure参数nginx和php是编译安装的关键.记录下来备用: php: ./configure --prefix=/usr/local/php --with-config-file-pat ...

  4. 【C#】课堂知识点#3

    1.讲解了实验1中,利用Char.is***来进行判断字符类型. using System; using System.Collections.Generic; using System.Linq; ...

  5. Istio旨在成为容器化微服务的网格管道

    在精彩的软件容器世界中,当新项目涌现并解决你认为早已解决的问题时,这感觉就像地面在你的脚下不断地移动.在许多情况下,这些问题很久以前被解决,但现在的云原生架构正在推动着更大规模的应用程序部署,这就需要 ...

  6. shell 学习笔记5-shell-if语句

    一.if条件语句 1.语法 1)单分支结构 第一种 if <条件表达式> then 指令 fi 第二种 if <条件表达式>:then 指令 fi 上文的"<条 ...

  7. Linux入职基础-1.2_U盘安装RedHat5具体步骤

    从U盘安装RedHat5具体步骤 从U盘安装RedHat Linux的具体步骤: 准备工作: RHEL_5.6_i386_DVD.ISO文件 具体步骤: 1.解压并用ultraiso软件打开rhel- ...

  8. Redis—.Net中的使用

    StackExchange.Redis使用以及封装 来源:http://www.cnblogs.com/qtqq/p/5951201.html,https://www.cnblogs.com/xsj1 ...

  9. NEST指定id

    1.默认以Id属性为Id,无Id属性则自动生成 2.可通过属性标签指定Id [ElasticsearchType(IdProperty = nameof(last_name))] public cla ...

  10. 外汇MT4编程手册

    1.为了最大的方便用户,交易中断的一些变量可以从智能系统输入. AccountNumber-账号(同义词:AccNum) Ask –卖价(买方出价) Balance – 交易账户的余额值 Bars – ...