MySQL|浅谈explain的使用

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 【7月更文挑战第11天】

前言

产品经理:“哎,最近上线的页面为啥那么慢,”。

小王:“经理,我先排查下,排查后给答复”。

从上面的对话中,可以看出来,作为一个运维工程师,小王需要排查SQL性能问题,针对性能问题,百度也有很多解决方案,例如查询性能优化SQL脚本、优化查询索引长度、复杂的JOIN操作简单化等等。

在解决这个问题前,我们在多数情况下需要使用关键字 EXPLAIN 来协助我们排查问题以及解决问题。

演示环境

当前所有操作均在都在 MySQL 8.0.31 中执行,如果有出入,还望指出。

操作环境:

  • 操作系统:MacOS 13.2
  • MySQL版本:MySQL 8.0.31
  • 安装MySQL方式:Homebrew
  • 演示库、表:db_test库、it_test1表

EXEPLAIN

在 MySQL 中,EXPLAIN 是一个非常有用的关键字,它可以帮助我们理解 已经写好的 SQL 查询的执行计划,进而优化查询,提升性能。通过 EXPLAIN 命令,可以查看 MySQL 如何使用索引,以及查询需要扫描多少行等等,这对于诊断和解决性能问题非常有帮助。

1 执行格式

在MySQL中,我们可以使用 explain 关键字后面跟上我们需要测试的SQL脚本,然后根据需要查询的信息来处理原SQL,来提升性能。

explain [SQL]

这里真得感谢发明这个SQL的大佬,就是一个关键字,再加上你需要执行的语句。例如:

mysql> explain select * from it_test1;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
|  1 | SIMPLE      | it_test1 | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
mysql>

2 格式详解

从上面的执行上来看,大体上有如下字段,下面这张图是从官网拿来的。

备注:图来自 https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

  • id:在SQL语句执行的过程中,每执行一次都会产生与关键字SELECT相对应的一个唯一ID,这个ID是顺序产生。
  • select_type:查询的类型。类型大体SIMPLE、PRIMARY、UNION、DEPENDENT UNION、UNION RESULT、SUBQUERY、DEPENDENT SUBQUERY、DERIVED、DEPENDENT DERIVED、MATERIALIZED、UNCACHEABLE SUBQUERY、UNCACHEABLE UNION。
  • table:涉及到的表的名称。
  • partitions:涉及到的分区信息。
  • type:单表时访问方法。
  • possible_keys:可能命中的key。
  • key:使用使用到的key。
  • key_len:使用使用到的key长度。
  • ref:当你使用索引key查询时,与索引匹配的对象的信息。
  • rows:预估匹配到的行的数量。
  • filtered:过滤列表示表条件过滤的表行的估计百分比。最大值为100,这意味着没有发生行过滤(有时时无效过滤)。从100开始递减的值表示过滤量增加。行显示检查的估计行数,行×过滤显示与下表连接的行数。例如,如果行为1000,过滤为50.00(50%),则要与下表连接的行数为1000×50%=500。
  • Extra:其他信息。

如果此时还不能解决或者消除你的疑问,建议可以参考下SELECT语句的用法,一看便知。

SELECT
    [ALL | DISTINCT | DISTINCTROW ]
    [HIGH_PRIORITY]
    [STRAIGHT_JOIN]
    [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
    [SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
    select_expr [, select_expr] ...
    [into_option]
    [FROM table_references
      [PARTITION partition_list]]
    [WHERE where_condition]
    [GROUP BY {col_name | expr | position}, ... [WITH ROLLUP]]
    [HAVING where_condition]
    [WINDOW window_name AS (window_spec)
        [, window_name AS (window_spec)] ...]
    [ORDER BY {col_name | expr | position}
      [ASC | DESC], ... [WITH ROLLUP]]
    [LIMIT {[offset,] row_count | row_count OFFSET offset}]
    [into_option]
    [FOR {UPDATE | SHARE}
        [OF tbl_name [, tbl_name] ...]
        [NOWAIT | SKIP LOCKED]
      | LOCK IN SHARE MODE]
    [into_option]
into_option: {
    INTO OUTFILE 'file_name'
        [CHARACTER SET charset_name]
        export_options
  | INTO DUMPFILE 'file_name'
  | INTO var_name [, var_name] ...
}

3 详细讲解

为了方便演示,这里创建多张表:it_test1 、 it_test2 、it_test3

--- 表it_test1以及数据
CREATE TABLE `it_test1` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '序号',
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
  `sex` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '性别',
  `age` int DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='演示-字符集'
insert into it_test1 values(null, 'zhang', '男', 20);
insert into it_test1 values(null, 'zhang', '男', 20);
insert into it_test1 values(null, 'wang', '女', 18);
insert into it_test1 values(null, 'li', '男', 20);
--- 表it_test2以及数据
CREATE TABLE `it_test2` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '序号',
  `t1_id` int(10) COMMENT 't1ID', 
  `grade` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '班级',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='演示-字符集2';
insert into it_test2 values(null, 1, '一年级');
insert into it_test2 values(null, 2, '一年级');
insert into it_test2 values(null, 3, '二年级');
insert into it_test2 values(null, 4, '三年级');

4 关键关注 type

无论SQL语句是如何的复杂,当我们执行这条语句时,最终我们可以看到的都是这对单表来操作,以及单表的操作详细。如下所示it_test1、it_test2两条记录:

mysql> explain select * from it_test1 t1 inner join it_test2 t2 on t1.id = t2.t1_id;
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref              | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
|  1 | SIMPLE      | t2    | NULL       | ALL    | NULL          | NULL    | NULL    | NULL             |    4 |   100.00 | Using where |
|  1 | SIMPLE      | t1    | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | db_test.t2.t1_id |    1 |   100.00 | NULL        |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql>

查询的访问类型,是较为重要的一个数据参数,该类型可以说直接反映你SQL语句执行的快慢。结果值从最高到最低依次为:

system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all

我们平时SQL调优的时候,一般得保证查询至少达到range级别,最好能达到ref

5 关键关注key和key_len

  • 索引长度

KEY为真实使用的索引。如果为NULL,表示没有使用索引。在上面的查询过程中,可以看到t1已经被命中 PRIMARY 类型的索引,结合索引长度可以看到为 4 。这里为什么是4呢?原来呀,这里是这个字段类型的实际数据最多占用的存储空间长度就是4(INT类型的长度),索引长度为索引中使用的字节数。key_len字段能够帮你检查是否充分的利用上了索引。ken_len越长,说明索引使用得越充分。

  • 索引长度为NULL

it_test2中就是NULL,主要是it_test1为主要驱动查询,而it_test2为被动驱动查询。

6 关键关注ref

一般情况下,这个也是我们需要关注的,例如当前查询下为 db_test.t2.t1_id 可以看出来,这里是执行命中到db_test库的t2表中的t1_id列。此时对应的匹配类型为 eq_ref (相等映射关联)。索引多的时候,此处比较复杂,本篇不在多述。

7 关键关注 filtered

filtered 表示某个表经过搜索条件过滤后剩余记录条数的百分比,如果使用的是索引执行的单表扫描,那么计算时需要估计出满足除使用到对应索引的搜索条件外的其他搜索条件的记录有多少条。

mysql> explain select * from it_test1 t1 inner join it_test2 t2 on t1.id = t2.t1_id where t2.t1_id = 1 ;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL        |
|  1 | SIMPLE      | t2    | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  |    4 |    25.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
mysql>

当我们增加一个限制条件 t2.t1_id = 1, 可以看到从 t2 表中扫描获取到 1 条信息,所占比例为1/4 也就是25 %。

8 关键关注 Extra

这里使用了一些额外的信息协助我们理解SQL脚本,例如条件查询、聚合函数、索引、扫描区间匹配、使用临时表、排序等等。下面列举一些常用的常看到的:

  • Using filesort(降低性能): MySQL 中无法利用索引完成的排序操作称为“文件排序”。
  • Using temporary(降低性能):使了用临时表保存中间结果,MySQL 在对查询结果排序时使用临时表。 常见于排序 order by 和分组查询 group by。
  • Using index(提高性能):Using index 代表表示相应的 select 操作中使用了覆盖索引(Covering Index), 避免访问了表的数据行, 效率不错!如果同时出现 using where, 表明索引被用来执行索引键值的查找;如果没有同时出现 using where, 表明索引只是用来读取数据而非利用索引执行查找。利用索引进行了排序或分组。
  • Using where:表明使用了 where 过滤。
  • Using join buffer:使用了连接缓存
  • impossible where where :子句的值总是 false, 不能用来获取任何元组。

总结

本篇简单讲述mysql中explain关键字的使用,以及explain关键字中我们常用的一些含义,结合调用情况作分析,可以结合查询情况来做进一步分析,本篇没有做数据量测试,只是简单描述,后续再写一篇从量上来测试。

【引用】

  1. [官网explain](!https://dev.mysql.com/doc/refman/8.0/en/execution-plan-information.html)
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
SQL 存储 关系型数据库
SQL优化之Explain详解(mysql)
`Explain`是MySQL中用于分析SQL查询执行计划的工具。它可以帮助我们了解MySQL如何执行SQL语句,包括如何使用索引、预计的行数以及查询的顺序。以下是`Explain`输出的关键列及其含义的简要摘要: 1. **id**:查询的序列号,表示查询中的子句层次,id越大优先级越高。 2. **select_type**:表示查询的类型,如SIMPLE(简单查询)、PRIMARY(主查询,多表查询中的第一个查询)、SUBQUERY(子查询)、DERIVED(派生表)或UNION(UNION操作的查询部分)。 3. **table**:查询涉及的表名,如果是子查询,可能显示为衍生表
61 0
|
2月前
|
SQL 关系型数据库 MySQL
mysql性能调优:EXPLAIN命令21
【7月更文挑战第21天】掌握SQL性能调优:深入解析EXPLAIN命令的神奇用法!
35 1
|
2月前
|
SQL 存储 数据库
MySQL设计规约问题之性能分析工具如Sql explain、show profile和mysqlsla在数据库性能优化中有什么作用
MySQL设计规约问题之性能分析工具如Sql explain、show profile和mysqlsla在数据库性能优化中有什么作用
|
2月前
|
SQL 索引 关系型数据库
MySQL设计规约问题之为什么推荐使用EXPLAIN来检查SQL查询
MySQL设计规约问题之为什么推荐使用EXPLAIN来检查SQL查询
|
3月前
|
SQL 关系型数据库 MySQL
MySQL数据库——索引(4)-SQL性能分析-profile详情、explain(profile查看指令,explain执行计划中各个字段的含义)
MySQL数据库——索引(4)-SQL性能分析-profile详情、explain(profile查看指令,explain执行计划中各个字段的含义)
41 2
|
3月前
|
SQL 存储 关系型数据库
MySQL的查询计划(EXPLAIN)
MySQL的查询计划(EXPLAIN)
43 2
|
4月前
|
存储 SQL 关系型数据库
MySQL 底层数据结构 聚簇索引以及二级索引 Explain的使用
MySQL 底层数据结构 聚簇索引以及二级索引 Explain的使用
58 0
|
4月前
|
SQL 缓存 关系型数据库
MySQL的万字总结(缓存,索引,Explain,事务,redo日志等)
MySQL的万字总结(缓存,索引,Explain,事务,redo日志等)
115 0
|
4月前
|
缓存 关系型数据库 MySQL
MySQL EXPLAIN详解
MySQL数据库是许多Web应用程序的底层支持,而查询性能的优化是确保系统高效运行的关键。在MySQL中,`EXPLAIN`是一项强大的工具,可帮助开发者深入了解查询语句的执行计划,从而更好地优化查询性能。本文将详细解析MySQL的`EXPLAIN`关键字,以揭开查询执行计划的面纱。
153 1
MySQL EXPLAIN详解
|
4月前
|
存储 关系型数据库 MySQL
MySQL技能完整学习列表6、查询优化——1、EXPLAIN命令的使用——2、索引优化
MySQL技能完整学习列表6、查询优化——1、EXPLAIN命令的使用——2、索引优化
41 0

相关产品

  • 云数据库 RDS MySQL 版
  • 下一篇
    DDNS