MySQL性能瓶颈排查定位实例详解

本文实例讲述了MySQL性能瓶颈排查定位的方法。分享给大家供大家参考,具体如下:

导读

从一个现场说起,全程解析如何定位性能瓶颈。

排查过程

收到线上某业务后端的MySQL实例负载比较高的告警信息,于是登入服务器检查确认。

1. 首先我们进行OS层面的检查确认

登入服务器后,我们的目的是首先要确认当前到底是哪些进程引起的负载高,以及这些进程卡在什么地方,瓶颈是什么。

通常来说,服务器上最容易成为瓶颈的是磁盘I/O子系统,因为它的读写速度通常是最慢的。即便是现在的PCIe SSD,其随机I/O读写速度也是不如内存来得快。当然了,引起磁盘I/O慢得原因也有多种,需要确认哪种引起的。

第一步,我们一般先看整体负载如何,负载高的话,肯定所有的进程跑起来都慢。

可以执行指令 w 或者 sar -q 1 来查看负载数据,例如:

[yejr@imysql.com:~ ]# w
 11:52:58 up 702 days, 56 min, 1 user, load average: 7.20, 6.70, 6.47
USER   TTY   FROM       LOGIN@  IDLE  JCPU  PCPU WHAT
root   pts/0  1.xx.xx.xx    11:51  0.00s 0.03s 0.00s w

或者 sar -q 的观察结果:

[yejr@imysql.com:~ ]# sar -q 1
Linux 2.6.32-431.el6.x86_64 (yejr.imysql.com)   01/13/2016   _x86_64_  (24 CPU)
02:51:18 PM  runq-sz plist-sz  ldavg-1  ldavg-5 ldavg-15  blocked
02:51:19 PM     4   2305   6.41   6.98   7.12     3
02:51:20 PM     2   2301   6.41   6.98   7.12     4
02:51:21 PM     0   2300   6.41   6.98   7.12     5
02:51:22 PM     6   2301   6.41   6.98   7.12     8
02:51:23 PM     2   2290   6.41   6.98   7.12     8

load average大意表示当前CPU中有多少任务在排队等待,等待越多说明负载越高,跑数据库的服务器上,一般load值超过5的话,已经算是比较高的了。

引起load高的原因也可能有多种:

某些进程/服务消耗更多CPU资源(服务响应更多请求或存在某些应用瓶颈);

发生比较严重的swap(可用物理内存不足);

发生比较严重的中断(因为SSD或网络的原因发生中断);

磁盘I/O比较慢(会导致CPU一直等待磁盘I/O请求);

这时我们可以执行下面的命令来判断到底瓶颈在哪个子系统:

[yejr@imysql.com:~ ]# top
top - 11:53:04 up 702 days, 56 min, 1 user, load average: 7.18, 6.70, 6.47
Tasks: 576 total,  1 running, 575 sleeping,  0 stopped,  0 zombie
Cpu(s): 7.7%us, 3.4%sy, 0.0%ni, 77.6%id, 11.0%wa, 0.0%hi, 0.3%si, 0.0%st
Mem: 49374024k total, 32018844k used, 17355180k free,  115416k buffers
Swap: 16777208k total,  117612k used, 16659596k free, 5689020k cached
 PID USER   PR NI VIRT RES SHR S %CPU %MEM  TIME+ COMMAND
14165 mysql   20  0 8822m 3.1g 4672 S 162.3 6.6 89839:59 mysqld
40610 mysql   20  0 25.6g 14g 8336 S 121.7 31.5 282809:08 mysqld
49023 mysql   20  0 16.9g 5.1g 4772 S 4.6 10.8  34940:09 mysqld

很明显是前面两个mysqld进程导致整体负载较高。

而且,从 Cpu(s) 这行的统计结果也能看的出来,%us 和 %wa 的值较高,表示当前比较大的瓶颈可能是在用户进程消耗的CPU以及磁盘I/O等待上。

我们先分析下磁盘I/O的情况。

执行 sar -d 确认磁盘I/O是否真的较大:

[yejr@imysql.com:~ ]# sar -d 1
Linux 2.6.32-431.el6.x86_64 (yejr.imysql.com)   01/13/2016   _x86_64_  (24 CPU)
11:54:32 AM  dev8-0  5338.00 162784.00  1394.00   30.76   5.24   0.98   0.19  100.00
11:54:33 AM  dev8-0  5134.00 148032.00 32365.00   35.14   6.93   1.34   0.19  100.10
11:54:34 AM  dev8-0  5233.00 161376.00  996.00   31.03   9.77   1.88   0.19  100.00
11:54:35 AM  dev8-0  4566.00 139232.00  1166.00   30.75   5.37   1.18   0.22  100.00
11:54:36 AM  dev8-0  4665.00 145920.00  630.00   31.41   5.94   1.27   0.21  100.00
11:54:37 AM  dev8-0  4994.00 156544.00  546.00   31.46   7.07   1.42   0.20  100.00

再利用 iotop 确认到底哪些进程消耗的磁盘I/O资源最多:

[yejr@imysql.com:~ ]# iotop
Total DISK READ: 60.38 M/s | Total DISK WRITE: 640.34 K/s
 TID PRIO USER   DISK READ DISK WRITE SWAPIN   IO>  COMMAND
16397 be/4 mysql    8.92 M/s  0.00 B/s 0.00 % 94.77 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
 7295 be/4 mysql   10.98 M/s  0.00 B/s 0.00 % 93.59 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14295 be/4 mysql   10.50 M/s  0.00 B/s 0.00 % 93.57 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14288 be/4 mysql   14.30 M/s  0.00 B/s 0.00 % 91.86 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320
14292 be/4 mysql   14.37 M/s  0.00 B/s 0.00 % 91.23 % mysqld --basedir=/usr/local/m~og_3320/mysql.sock --port=3320

可以看到,端口号是3320的实例消耗的磁盘I/O资源比较多,那就看看这个实例里都有什么查询在跑吧。

2. MySQL层面检查确认

首先看下当前都有哪些查询在运行:

[yejr@imysql.com(db)]> mysqladmin pr|grep -v Sleep
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| Id |User| Host   | db |Command|Time | State    | Info                                             |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+
| 25 | x | 10.x:8519 | db | Query | 68 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404612 order by Fvideoid) t1 |
| 26 | x | 10.x:8520 | db | Query | 65 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>484915 order by Fvideoid) t1 |
| 28 | x | 10.x:8522 | db | Query | 130 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>404641 order by Fvideoid) t1 |
| 27 | x | 10.x:8521 | db | Query | 167 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324157 order by Fvideoid) t1 |
| 36 | x | 10.x:8727 | db | Query | 174 | Sending data | select max(Fvideoid) from (select Fvideoid from t where Fvideoid>324346 order by Fvideoid) t1 |
+----+----+----------+----+-------+-----+--------------+-----------------------------------------------------------------------------------------------+

可以看到有不少慢查询还未完成,从slow query log中也能发现,这类SQL发生的频率很高。

这是一个非常低效的SQL写法,导致需要对整个主键进行扫描,但实际上只需要取得一个最大值而已,从slow query log中可看到:

Rows_sent: 1  Rows_examined: 5502460
每次都要扫描500多万行数据,却只为读取一个最大值,效率非常低。

经过分析,这个SQL稍做简单改造即可在个位数毫秒级内完成,原先则是需要150-180秒才能完成,提升了N次方。

改造的方法是:对查询结果做一次倒序排序,取得第一条记录即可。而原先的做法是对结果正序排序,取最后一条记录,汗啊。。。

写在最后,小结

在这个例子中,产生瓶颈的原因比较好定位,SQL优化也不难,实际线上环境中,通常有以下几种常见的原因导致负载较高:

一次请求读写的数据量太大,导致磁盘I/O读写值较大,例如一个SQL里要读取或更新几万行数据甚至更多,这种最好是想办法减少一次读写的数据量;

SQL查询中没有适当的索引可以用来完成条件过滤、排序(ORDER BY)、分组(GROUP BY)、数据聚合(MIN/MAX/COUNT/AVG等),添加索引或者进行SQL改写吧;

瞬间突发有大量请求,这种一般只要能扛过峰值就好,保险起见还是要适当提高服务器的配置,万一峰值抗不过去就可能发生雪崩效应;

因为某些定时任务引起的负载升高,比如做数据统计分析和备份,这种对CPU、内存、磁盘I/O消耗都很大,最好放在独立的slave服务器上执行;

服务器自身的节能策略发现负载较低时会让CPU降频,当发现负载升高时再自动升频,但通常不是那么及时,结果导致CPU性能不足,抗不过突发的请求;

使用raid卡的时候,通常配备BBU(cache模块的备用电池),早期一般采用锂电池技术,需要定期充放电(DELL服务器90天一次,IBM是30天),我们可以通过监控在下一次充放电的时间前在业务低谷时提前对其进行放电,不过新一代服务器大多采用电容式电池,也就不存在这个问题了。

文件系统采用ext4甚至ext3,而不是xfs,在高I/O压力时,很可能导致%util已经跑到100%了,但iops却无法再提升,换成xfs一般可获得大幅提升;

内核的io scheduler策略采用cfq而非deadline或noop,可以在线直接调整,也可获得大幅提升。

更多关于MySQL相关内容感兴趣的读者可查看本站专题:《MySQL日志操作技巧大全》、《MySQL事务操作技巧汇总》、《MySQL存储过程技巧大全》、《MySQL数据库锁相关技巧汇总》及《MySQL常用函数大汇总》

希望本文所述对大家MySQL数据库计有所帮助。

(0)

相关推荐

  • Mysql性能优化案例 - 覆盖索引分享

    场景 产品中有一张图片表,数据量将近100万条,有一条相关的查询语句,由于执行频次较高,想针对此语句进行优化 表结构很简单,主要字段: 复制代码 代码如下: user_id 用户ID picname 图片名称 smallimg 小图名称 一个用户会有多条图片记录 现在有一个根据user_id建立的索引:uid 查询语句也很简单:取得某用户的图片集合 复制代码 代码如下: select picname, smallimg from pics where user_id = xxx; 优化前 执行查

  • my.ini优化mysql数据库性能的十个参数(推荐)

    今天刚好需要配置mysql 5.5.45,因为数据库量挺大的,所以必须优化,要不mysql真的不快. (1).max_connections: 允许的同时客户的数量.增加该值增加 mysqld 要求的文件描述符的数量.这个数字应该增加,否则,你将经常看到 too many connections 错误. 默认数值是100,我把它改为1024 . (2).record_buffer: 每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区.如果你做很多顺序扫描,你可能想要增加该值.默认

  • Mysql性能优化案例研究-覆盖索引和SQL_NO_CACHE

    场景 产品中有一张图片表pics,数据量将近100万条,有一条相关的查询语句,由于执行频次较高,想针对此语句进行优化 表结构很简单,主要字段: 复制代码 代码如下: user_id 用户ID picname 图片名称 smallimg 小图名称 一个用户会有多条图片记录,现在有一个根据user_id建立的索引:uid,查询语句也很简单:取得某用户的图片集合: 复制代码 代码如下: select picname, smallimg from pics where user_id = xxx; 优化

  • Mysql性能优化方案分享

    网上有不少mysql 性能优化方案,不过,mysql的优化同sql server相比,更为麻烦,同样的设置,在不同的环境下 ,由于内存,访问量,读写频率,数据差异等等情况,可能会出现不同的结果,因此简单地根据某个给出方案来配置mysql是行不通的,最好能使用status信息对mysql进行具体的优化. mysql> show global status; 可以列出MySQL服务器运行各种状态值,另外,查询MySQL服务器配置信息语句: mysql> show variables; 一.慢查询

  • MySql删除和更新操作对性能有影响吗

    删除和更新操作的开销往往比插入高,所以一个好的设计需要减少对数据库的更新和删除操作. 3.1更新操作 数据库的更新操作会带来一连串的"效应":更新操作需要记录日志(以便错误时回滚):更新可变长字段(如,varchar类型)会带来数据物理存储的变化(记录的移动):更新索引字段会导致索引重建:更新主键会导致数据重组等.这一切不但会造成更新操作本身效率低,而且由于磁片碎片的产生会造成以后查询性能的降低.为了应对这一情况,有两种策略:一.减少更新次数,把多个字段的更新写到同一个语句里:二.避免

  • mysql性能优化之索引优化

    作为免费又高效的数据库,mysql基本是首选.良好的安全连接,自带查询解析.sql语句优化,使用读写锁(细化到行).事物隔离和多版本并发控制提高并发,完备的事务日志记录,强大的存储引擎提供高效查询(表记录可达百万级),如果是InnoDB,还可在崩溃后进行完整的恢复,优点非常多.即使有这么多优点,仍依赖人去做点优化,看书后写个总结巩固下,有错请指正. 完整的mysql优化需要很深的功底,大公司甚至有专门写mysql内核的,sql优化攻城狮,mysql服务器的优化,各种参数常量设定,查询语句优化,主

  • 安装配置MySQLMTOP来监控MySQL运行性能的教程

    一.环境说明 1.服务器角色 2.系统环境 CentOS 6.2 x86_64 3.环境要求 (1).MySQL 5.0 及以上       (用来存储监控系统采集的数据) (2).Apache 2.2 及以上        (WEB 服务器运行服务器) (3).PHP 5.3 以上                (WEB 界面) (4).Python 2                       (推荐 2.7 版本,其他版本未做测试,执行数据采集和报警任务) (5).MySQLdb for

  • 19个MySQL性能优化要点解析

    以下就是跟大家分享的19个MySQL性能优化主要要点,一起学习学习. 1.为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的.当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了. 这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的.因为,我们某些查询语句会让MySQL不使用缓存.请看下面的示例: // 查询缓存不开启 $r = mysq

  • MySQL中join语句的基本使用教程及其字段对性能的影响

    join语句的基本使用 SQL(MySQL) JOIN 用于根据两个或多个表中的字段之间的关系,从这些表中得到数据. JOIN 通常与 ON 关键字搭配使用,基本语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1 通常称为左表,table2 称为右表.ON 关键字用于设定匹配条件,用于限定在结果集合中想要哪些行.如果需要指定其他条件,后面可以加上 WHERE 条件 或者 LIMIT 以限制记录返回数目等.

  • MySQL中distinct语句去查询重复记录及相关的性能讨论

    在 MySQL 查询中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值. 关键词 DISTINCT 用于返回唯一不同的值,就是去重啦.用法也很简单: SELECT DISTINCT * FROM tableName DISTINCT 这个关键字来过滤掉多余的重复记录只保留一条. 另外,如果要对某个字段去重,可以试下: SELECT *, COUNT(DISTINCT nowamagic) FROM table GROUP BY nowamagic 这个用

  • Mysql数据库性能优化一

    今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能.这里,我们不会讲过多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库. mysql的性能优化无法一蹴而就,必须一步一步慢慢来,从各个方面进行优化,最终性能就会有大的提升. Mysql数据库的优化技术 对mysql优化是

  • 用percona-toolkit为MySQL收集系统和性能信息的教程

    系统类工具 1.         pt-diskstats 功能介绍: 是一个对GUN/LINUX的交互式监控工具 用法介绍: pt-diskstats [OPTION...] [FILES] 为GUN/LINUX打印磁盘io统计信息,和iostat有点像,但是这个工具是交互式并且比iostat更详细.可以分析从远程机器收集的数据. 使用示例: 范例1:查看本机所有的磁盘的状态情况: pt-diskstats 范例2:只查看本机sda2磁盘的状态情况 pt-diskstats --devices

  • MySQL中使用SHOW PROFILE命令分析性能的用法整理

    show profile是由Jeremy Cole捐献给MySQL社区版本的.默认的是关闭的,但是会话级别可以开启这个功能.开启它可以让MySQL收集在执行语句的时候所使用的资源.为了统计报表,把profiling设为1 mysql> SET profiling = 1; 之后在运行一个查询 mysql> SELECT COUNT(DISTINCT actor.first_name) AS cnt_name, COUNT(*) AS cnt -> FROM sakila.film_act

  • Mysql数据库性能优化二

    在上篇文章给大家介绍了mysql数据库性能优化一,今天继续接着上篇文章给大家介绍数据库性能优化相关知识.具体内容如下所示: 建立适当的索引 说起提高数据库性能,索引是最物美价廉的东西了.不用加内存,不用改程序,不用调sql,只要执行个正确的'create index',查询速度就可能提高百倍千倍,这可真有诱惑力.可是天下没有免费的午餐,查询速度的提高是以插入.更新.删除的速度为代价的,这些写操作,增加了大量的I/O. 是不是建立一个索引就能解决所有的问题?ename上没有建立索引会怎样? sel

  • MySQL中distinct与group by之间的性能进行比较

    最近在网上看到了一些测试,感觉不是很准确,今天亲自测试了一番.得出了结论,测试过程在个人计算机上,可能不够全面,仅供参考. 测试过程: 准备一张测试表 CREATE TABLE `test_test` ( `id` int(11) NOT NULL auto_increment, `num` int(11) NOT NULL default '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1

随机推荐