通常关于io相关的问题,我们可以使用top及iostat进行分析系统CPU及磁盘IO情况,然后使用strace lsof等工具进行追踪,以及新的工具 filetop及opensnoop,进行分析write及open的函数追踪
我们这次拿的案例,是一个数据库的性能案例,一个基于Python Flask的商品搜索应用,商品信息存在于MySQL中,应用可以通过MySQL进行查询
准备了两个容器,一个MySQL数据库实例,一个商品搜索实例
商品搜索的应用以HTTP的形式提供一个接口
支持的接口有
/products 查询指定商品的信息
我们首先在一台虚拟机上启动这些Docker实例
然后另一台作为客户端,去请求单词,查看热度
我们尝试搜索对应的商品
curl http://192.168.0.10:10000/products/helloworld
返回的数据为0,且处理时间超过15秒,这是为什么呢?
15秒的慢查询,一看就不对,我们需要对其进行相关的查找和修改
我们现将curl命令放在一个循环中执行,同时为了避免压力过大,在每次的查询之后,等待5秒,之后进行新的请求
while true:
do
curl http://192.168.0.10:10000/products/helloworld;
sleep 5;
done
在之后进行分析为何响应变慢
首先是top命令,分析系统的CPU使用情况
获得输出如上,可以看出top的输出中,CPU的iowait都比较高,特别是CPU0,超过60%
但CPU使用率并不高,最高只有1.7%
既然发现了IO的问题,我们就继续使用iostat进行查看io相关问题
iostat -d -x 1
$ iostat -d -x 1
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util … sda 273.00 0.00 32568.00 0.00 0.00 0.00 0.00 0.00 7.90 0.00 1.16 119.30 0.00 3.56 97.20 |
在其中,sda的每秒读写大小为32MB/S,IO使用率为97% 发现了是io性能
然后我们使用对应的pidstat来查看是哪个进程导致的IO升高
pidstat -d 1
pidstat中输出了PID为27458的mysqld的进程正在进行大量的读,读取的速度为32MB/s
跟刚才的iostat的输出对比发现,根源自然是mysqld进程
继续使用老一套的打法,我们接下来使用strace进行查看mysqld的执行
而且因为mysql是一个多线程的数据库应用,执行strace的时候,别忘了加上 -f参数
strace -f -p 27458
发现其正在读取大量的数据,这时候呢,就是lsof进行上场的时候
lsof -p 27458查看对应的lsof的输出(这一步要给出的是对应的pid,而非是线程号,如果不知道pid,可使用pstree 查看)
获取到对应的输出
mysqld 27458 999 38u REG 8,1 512440000 2601895 /var/lib/mysql/test/products.MYD
上面的输出中,32u是说明了mysqld是以读写的方式访问的文件,
MYD则是MyISAM引擎用于存储表数据的文件
MYI 是存储表索引的
frm 是表的元数据
opt是存储数据库的元信息
文件名就是数据表的名字
文件的父目录,就是数据库的名字
这说明mysql在使用test库中的products的表
那么我们需要进入mysqld中进行查看,分析mysqld执行的数据
执行show prcesslist命令,查看正在执行的SQL语句
获取到下面的执行sql
在对应的输出中
info中说明了完整的SQL语句
那么这个语句如果出现了慢查询的情况,第一可能性就是没有索引
故,我们使用explain命令查看是否没有索引
key为null,故真的没有索引
而且这个字段是一个text字段,我们需要给其建立一个前缀索引
CREATE INDEX products_index on products(productName(64));
这样,索引就基本建好了,我们可以检查一下性能问题
检查可以发现基本就没有性能问题了
这样,我们解决了因为mysql查询无索引导致的io性能问题
除此外,还额外提供一个小的问题Demo
就是有一个应用
在每次读取文件之前,会将/proc/sys/vm/drop_caches改为1
这样会导致 /proc/sys/vm/drop_cache 释放pageCache,即文件缓存,导致mysql的读取数据每次都要从磁盘重新读取