MySQL性能分析工具的使用

64 阅读6分钟

1. 数据库服务器的优化步骤

当我们遇到数据库调优问题的时候,该如何思考呢?这里把思考的流程整理成下面这张图。

整个流程划分成了 观察(Show status) 和 行动(Action) 两个部分。字母 S 的部分代表观察(会使

用相应的分析工具),字母 A 代表的部分是行动(对应分析可以采取的行动)。

2. 查看系统性能参数

在MySQL中,可以使用 SHOW STATUS 语句查询一些MySQL数据库服务器的 性能参数 、 执行频率 。

SHOW STATUS语句语法如下:

SHOW [GLOBAL|SESSION] STATUS LIKE '参数';

3.(重要) 统计SQL的查询成本:last_query_cost

我们依然使用第8章的 student_info 表为例:

CREATE TABLE `student_info`
(
    `id`          INT(11) NOT NULL AUTO_INCREMENT,
    `student_id`  INT     NOT NULL,
    `name`        VARCHAR(20) DEFAULT NULL,
    `course_id`   INT     NOT NULL,
    `class_id`    INT(11)     DEFAULT NULL,
    `create_time` DATETIME    DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE = INNODB
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8;

如果我们想要查询 id=900001 的记录,然后看下查询成本,我们可以直接在聚簇索引上进行查找:

SELECT student_id, class_id, NAME, create_time
FROM student_info
WHERE id = 900001;

运行结果(1 条记录,运行时间为 0.042s )

然后再看下查询优化器的成本,实际上我们只需要检索一个页即可:

如果我们想要查询 id 在 900001 到 9000100 之间的学生记录呢?

SELECT student_id, class_id, NAME, create_time
FROM student_info
WHERE id BETWEEN 900001 AND 900100;

运行结果(100 条记录,运行时间为 0.046s ):

然后再看下查询优化器的成本,这时我们大概需要进行 20 个页的查询。

你能看到页的数量是刚才的 20 倍,但是查询的效率并没有明显的变化,实际上这两个 SQL 查询的时间

基本上一样,就是因为采用了顺序读取的方式将页面一次性加载到缓冲池中,然后再进行查找。虽然 页

数量(last_query_cost)增加了不少 ,但是通过缓冲池的机制,并 没有增加多少查询时间 。

使用场景: 它对于比较开销是非常有用的,特别是我们有好几种查询方式可选的时候。

4. 定位执行慢的 SQL:慢查询日志

开启慢查询日志参数

  1. 开启slow_query_log
mysql > set global slow_query_log='ON';

然后我们再来查看下慢查询日志是否开启,以及慢查询日志文件的位置:

你能看到这时慢查询分析已经开启,同时文件保存在 /var/lib/mysql/atguigu02-slow.log 文件

中。

  1. 修改long_query_time阈值

接下来我们来看下慢查询的时间阈值设置,使用如下命令:

mysql > show variables like '%long_query_time%';

这里如果我们想把时间缩短,比如设置为 1 秒,可以这样设置:

#测试发现:设置global的方式对当前session的long_query_time失效。对新连接的客户端有效。所以可以一并 执行下述语句 
mysql > set global long_query_time = 1; mysql> show global variables like '%long_query_time%'; 
mysql> set long_query_time=1; 
mysql> show variables like '%long_query_time%';

查看慢查询数目

查询当前系统中有多少条慢查询记录

SHOW GLOBAL STATUS LIKE '%Slow_queries%';

测试及分析

  1. 测试

从上面的结果可以看出来,查询学生编号为“3455655”的学生信息花费时间为2.09秒。查询学生姓名为

“oQmLUr”的学生信息花费时间为2.39秒。已经达到了秒的数量级,说明目前查询效率是比较低的,下面的小节我们分析一下原因。

  1. 分析
show status like 'slow_queries';

慢查询日志分析工具:mysqldumpslow

在生产环境中,如果要手工分析日志,查找、分析SQL,显然是个体力活,MySQL提供了日志分析工具

mysqldumpslow 。

查看mysqldumpslow的帮助信息

mysqldumpslow --help

mysqldumpslow 命令的具体参数如下:

举例:我们想要按照查询时间排序,查看前五条 SQL 语句,这样写即可:

mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01-slow.log

工作常用参考:

#得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
#得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
#得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log
#另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more

5. 查看 SQL 执行成本:SHOW PROFILE

6. 分析查询语句:EXPLAIN

基本语法

EXPLAIN 或 DESCRIBE语句的语法形式如下:

EXPLAIN SELECT select_options 
或者
DESCRIBE SELECT select_options

如果我们想看看某个查询的执行计划的话,可以在具体的查询语句前边加一个 EXPLAIN ,就像这样:

mysql> EXPLAIN SELECT 1;

查看执行计划并不会真的查询,只是展示数据库怎么去查询而已。

EXPLAIN 语句输出的各个列的作用如下:

数据的准备

EXPLAIN各列作用

为了让大家有比较好的体验,我们调整了下 EXPLAIN 输出列的顺序。

1. table

不论我们的查询语句有多复杂,里边儿 包含了多少个表 ,到最后也是需要对每个表进行 单表访问 的,所

以MySQL规定EXPLAIN语句输出的每条记录都对应着某个单表的访问方法,该条记录的table列代表着该

表的表名(有时不是真实的表名字,可能是简称)。

2. id

union all 就只有两个 id ,因为不需要去重操作

小结:

  • id如果相同,可以认为是一组,从上往下顺序执行 (相当于并列,一起查询,只需要查一次)
  • 在所有组中, id值越大,优先级越高,越先执行
  • 关注点: id号每个号码,表示一趟独立的查询, 一个sql的查询趟数越少越好

3. select_type

SIMPLE

PRIMARY、UNION

SUBQUERY(普通子查询)

DEPENDENT SUBQUERY (相关子查询)

DERIVED

4. partitions (可略)

5. type ☆(重要)

完整的访问方法如下: system(Hash索引) , const (唯一索引+等值匹配), eq_ref , ref , fulltext , ref_or_null ,

index_merge , unique_subquery , index_subquery , range , index , ALL 。

system (类似Hash索引的,基本上直接就能定位到的)

const(找到就马上返回,注意点:唯一索引、等值匹配、单表)

key2不是唯一索引,那么type就是ALL(全表扫描)

eq_ref (唯一索引、等值匹配、多表)

ref(索引 + 与数据类型一样的常量等值匹配)

ref_or_null

index_merge

range(范围级别)

index(索引覆盖)

ALL(没有索引)

小结:

结果值从最好到最坏依次是: system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 其中比较重要的几个提取出来(见上图中的蓝色)。

SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,最好是 consts级别。(阿里巴巴

开发手册要求)

6. possible_keys和key

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';

9. rows ☆

10. filtered

主要看多表连接

11. Extra ☆

Impossible WHERE

mysql> EXPLAIN SELECT * FROM s1 WHERE 1 != 1;

Using where

No matching min/max row

Using index

加上主键值也算是索引覆盖

7. EXPLAIN的进一步使用