概说“应用反应某些操作或查询慢”的处理方法(从MySQL DBA角度)

概谈“应用反应某些操作或查询慢”的处理方法(从MySQL DBA角度)

        作为一个运维人员或多或少都会从应用开发那边得到类似“为什么用户反应很多操作慢?或者为什么我这里统计的日志里面出现了较多的查询较慢?”。那么遇到此问题我们该怎么来解决呢?我今天就利用自己的工作经验从MySQL DBA的角度来简单分析一下这个问题。

        首先我们得区分这种查询慢或者操作慢发生的频率怎么样。按理来说这种状况不会经常发生,一个产品上线前都有专门的QA团队对其进行功能测试/压力测试等等,所以绝大部分的时候是偶尔出现或者有规律的出现(比如是某一类操作相应时间特长),因为测试路径是不可能被全部覆盖的,总会存在遗漏的地方。那么当这种小概率事件发生以后我们该怎么做呢?在说具体的方法之前我们得大概知道一点,发生响应慢这种状况绝大部分情况都是由于整个系统(不光止OS)肯定某些地方存在瓶颈需要我们去消除,比如从MySQL DBA的角度来看我就会去关注网络/IO/内存/CPU/数据库内部状态(比如锁争用,锁等待,buffer 太小等等)。下面我就具体介绍一下我自己平常怎么去查看这些方面的状态。

        网络,这是一个很泛,内容也很多的东西。在我的工作中我主要关注两方面的内容网络延迟和网卡流量。网络延迟最简单也绝大部分时候能解决问题的工具是ping,比如从app server上ping一下db server看一下两者之间大概网络延迟有没有丢包等等信息。那么你可能有疑问(也包括我)多少的延迟才算合理的,也就是延迟有没有一个基本的比较通用的参考数据呢?我从网友 @yzsind-叶正盛 的微博上有看到(不知参考性怎样,有待确认):

“内部网络我经常用的快速估算公式是:同交换机≈0.1ms,同机房≈0.2ms,同城机房≈0.5~1ms,远距离≈(两地线路距离/100) ms”

那么网卡流量该怎么查看呢?首先得知道自己是千兆网卡或者万兆网卡,查看方式:

repls@repls-tp:~$ lspci | grep Ethernet
00:19.0 Ethernet controller: Intel Corporation 82579LM GigabitNetwork Connection (rev 04)

红色部分显示了当前网卡是千兆网卡,但是我们还得注意一点,平常说千兆网卡那么它到底可以跑满多少流量呢?你可千万注意千兆网卡跑完流量只有1024M/8左右的流量,因为我们平时说的千兆不是以byte计算而是bit,所以还要除8。我们可以通过sar(号称linux系统管理监控里面的瑞士军队)来观察网卡流量:

repls@repls-tp:~$ sar -n DEV 2
Linux 3.5.0-17-generic (repls-tp) 2013年01月14日_x86_64_ (4 CPU)


14时36分04秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
14时36分06秒      eth0      0.00      0.00      0.00      0.00      0.00      0.00      0.00
14时36分06秒        lo      1.00      1.00      0.15      0.15      0.00      0.00      0.00
14时36分06秒     wlan0      3.50      4.00      0.55      1.04      0.00      0.00      0.00

对于sar的用法远远不止这么简单,你还可以查看道丢包之类更丰富的信息,详情可以man sar或者google查看。


        IO,存储对于数据库来说是很重要的一点,很多时候瓶颈都会是IO能力不足导致。那么有什么方法大概可以判断出是由于系统IO能力不足呢?(术语应该称为IO-bound吧)最简单也是最常用的工具就是iostat,我一般会加-x选项(表示显示更多详情):

repls@repls-tp:~$ iostat -x 2
Linux 3.5.0-17-generic (repls-tp) 2013年01月14日_x86_64_ (4 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.42    0.00    4.36    0.53    0.00   82.69
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.15     1.14    0.78    2.04    18.62    30.05    34.48     0.11   37.73   20.70   44.28   5.01   1.41

这里主要解释Device这行中几个平常关注得多的列。

r/s,w/s:每秒写,每秒读的次数加起来也就是常说的iops。

rrqm/s,wrqm/s:每秒merge读,merge写的次数,这个主要是IO调度器来完成的事。

await:响应时间,这个主要是指的硬件层面一个IO的响应时间,这个值一般多少合理?看应用需求吧,我也没多少经验有人说20ms以内,有人说10ms以内。

r_await,w_await:这个是哪个sysstat新增的?貌似之前用公司机器没有这两列,看意思应该就是读延迟,写延迟吧。

svctm:IO请求的服务时间,这个值肯定会比await小。

%util:磁盘设备繁忙程度,如果对于单个盘来说100%表示磁盘已经忙不过来,但是如果底层做了raid的话100%的意义就不明确了。


刚说了%util=100对于底层做了raid意义不明确,此时可以结合vmstat来看。

repls@repls-tp:~$ vmstat 2
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 2  0      0 721676 175460 1214868    0    0    11    17  181  170 12  4 83  1
 1  0      0 716032 175460 1220044    0    0     0     0 1184 2669  7  3 90  0
 0  0      0 717332 175460 1219696    0    0     0    18 1169 2671  6  3 91  0

vmstat的结果分成了几个部分,在最上面一行给出。这里我说说自己关注比较多的列。

b:blocking,前面说了底层做了raid iostat的%util参考意义不是非常大,因为可以结合这个参数,如果b的值大于磁层有效物理磁盘个数,那么基本上就可以表示系统IO能力不足了。

r:running,cup队列中running的进程数,这个如果大于cpu的核数基本上代表了cpu不够。

si,so: swap in,swap out,表示swap使用情况,如果有的话表示内存不够或者内存使用不合理(比如在MySQL中flush method=O_DIRECT没有打开等等)。

us:cpu time中user占用的部分,理论上是要尽量的将cpu time都花在user进程上。

sy:cpu time中sys占用的部分,如果这个比较高就得分析一下是什么原因导致了的。

id:idle

wa:wait time这个值不宜过高,比如高于20或者30就得查原因了。


        内存:关于内存这个懂的不是很深,linux中是采用vm实现。也就是刚开始分配内存是分配的是虚拟内存(刚开始分配的内存地址是虚址,直到真正使用到时才会有实址的),所以这样的好处就是系统能使用比实际物理RAM更大的地址空间。我一般通过free 和vmstat查看系统的内存状态。

repls@repls-tp:~$ free -m
             total       used       free     shared    buffers     cached
Mem:          5853       5168        685          0        172       1190
-/+ buffers/cache:       3805       2048
Swap:         6026          0       6026

这里需要明明几点吧。第一,在free输出结果中第二行(Mem开头)的是从系统层面看到的内存使用状态,也就是说即使某部分内存并没有进程在使用它,但是为了cache加速以后的访问,所以我们会看到free列比较小,cached列比较大。第二,第三行(-/+开头)的是从应用程序角度看到的内存使用状态,所以如果你平常关注过那么会发现这行的free明明很多但是在第二行的free里面却很小。这就是那部分的内存被当作cache用的缘故。

当然通过free看到的信息只是一个整体性的,如果你想查看更详细的那么可以通过cat /proc/vmstat查看。

顺便提一点:在linux中cache与buffer的区别是什么?2.6以前这个概念区分还是有意义的,那时应该是考虑到inode的大小和内存page大小不一至,为了更好的利用内存空间于是设定了两种page大小的缓冲,这个就类似于Oracle里的buffer有8k页的缓存,有16k一样吧。为什么现在模糊了这种概念,我个人猜测是因为内存的廉价,空间的增大为了节省这么点内存空间维护两种大小page的缓存不值得。所以现在谈这buffer与cache的区别意义不大。关于这点解释得不合理也请指正。


        CPU:对cpu的理解就更浅了,平常也仅仅就关注进程跑了几个cpu,是哪几个CPU跑满了,每个CPU的user time与sys time怎么样?至于有见网友关注得比较多的中断分布情况我自己还没怎么去研究。这里就不多说了,希望以后知识扎实以后不上来,不过可以提几个工具查看:top mpstat等等。


        数据库运行状态:其实这又是一个很范,涉及知识很多的点。因为我这篇文章主要是解决这种偶遇的响应慢问题,所以说说常见的几个关注点。

1. 我们需要将现场的SQL得到(不然我怎么可以确认事发当时SQL响应本身有我问题),有几种方法,show processlist/show engine innodb status/tcpdump抓包。由于前两者对于抓执行时间很快的SQL效果不怎么样,所以可以结合tcpdump抓包然后利用pt-query-digest来分析。

2. 数据库内部状态可以关注较多的status变量,比如thread_running,thread_created这些为接下来的参数调优提供依据。

3. 利用show engine innodb status来看看buffer pool的命中率,redo log flush速度,内部lock情况(对innodb lock这部分还不熟,没经验,提出来是因为看到同行有人这么做)。

       那么上面几个方面的状态获取我们该是一个怎么样的步骤去得到呢?我个人偏向于先从整体的,容易一眼就看出来的开始,比如先是系统整体负载情况,IO情况,CPU情况,网络情况等等,这些一眼看出来没多大问题再是数据库内部去或许一些运行状态信息,获取事发当时的SQL信息等等。另外建议大家也包括我自己以后要更加关注并使用percona toolkit,它确实能提高我们的工作效率,超赞的工具集!同时我们平常更应该做好监控工作,分析慢日志,分析监控的数据信息,尽早的将可能出现的问题扼杀在摇篮中。

        当然每个人都有自己解决问题的方法/思路,如果我的思路存在什么问题欢迎大家交流指出~