MySQL系列——MySQL的执行计划

基于 MySQL 8.0 community 的版本

EXPLAIN 摘要

EXPLAIN 来可以拿到 MySQL 是怎样执行 SQL 语句的。EXPLAIN 会返回一个表,表中的每一行都表示一个 select 语句的执行的信息。

为了更生动的说明这个 MySQL 的执行计划,我用具体的例子阐述,我在本地建一个叫 school 的数据库,并建立 4 张表,分别是学生表,教师表, 分数表,课程表,写一些测试数据进去。

执行脚本文章末尾。

EXPLAIN 输出的格式说明

有的测试的数据库,现在我们针对 EXPLAIN 的输出做出一些说明和描述,理解一下 MySQL 的执行计划。我们可以简单执行一下 SQL 语句:explain select * from student;
输出如下:
EXPLAIN 输出
EXPLAIN 输出字段说明

  • id
    id 就是生成的 select 查询的序号,如果 id 号相同,则从上往下执行;如果 id 不同,id 越大,优先级越高,越先被执行。比如:
    相同 select id
    不同的 select id
  • select_type
    select_type 主要是用来分辨查询的类型,是普通查询还是联合查询还是子查询,又有以下几种类型:
    • SIMPLE:简单的查询,不包含子查询和 union 查询

    • PRIMARY:查询中若包含任何复杂的子查询,最外层查询则被标记为 PRIMARY

    • UNION:第二个或者之后的 select 查询标记为 UNION

    • DEPENDENT UNION:跟 UNION 类似,dependent 表示 union 或 union all 联合而成的结果会受到外部表影响

    • UNION RESULT:从 union 表获取结果的 select

    • SUBQUERY:在 select 或者 where 列表中包含子查询

      子查询等于一个值得时候就是 subquery

    • DEPENDENT SUBQUERY: SUBQUERY 的子查询要受到外部表查询的影响

      子查询返回的是值得集合就是 DEPENDENT SUBQUERY

    • DERIVED:衍生,from 子句中出现的子查询,也叫派生类

    • DEPENDENT DERIVED:DERIVED 的子查询受到外部衍生表的影响

    • MATERIALIZED:物化子查询,子查询来自视图

    • UNCACHEABLE SUBQUERY:表示使用子查询的结果不能被换缓存

    • UNCACHEABLE UNION:表示使用 union 的查询的结果不能被缓存

  • partitions
    分区,分库分表才会有该值
  • table
    对应行正在访问哪一个表,表名或者别名,可能还是临时表或者 union 合并的结果集。
    如果是具体的表名,则是从物理表中获取的数据;
    如果是类似 derivedN 的形式,表示使用了 id 为 N 的查询产生的衍生表;
    当有 union 结果集的时候,表名是 union n1,n2 等形式,n1,n2 表示参与 union 的 id;
  • type
    表示 MySQL 在表中找到所需行的方式,或者叫访问类型,常见的类型有以下几种,从上到下性能依次到好
    • ALL:
      全表扫描
      全表扫描.png
    • index:
      索引全扫描,遍历整个索引来查询匹配的行
      索引全扫描.png
      主要有两种情况:
      一种是当前的查询时覆盖索引,即我们需要的数据在索引中就可以索取;
      另外一种是使用了索引进行排序,这样就避免数据的重排序;
    • range:
      表示利用索引查询的时候限制了范围,在指定范围内进行查询,避免了 index 的全索引扫描。
      适用的操作符:
      =, <>, > , >=, <, <=, IS NULL, BETWEEN, LIKE, or IN ()
    • index_subquery:
      利用索引来关联子查询,不再扫描全表
    • unique_subquery:
      该连接类型类似于 index_subquery,使用的是唯一索引
    • index_merge:
      在查询过程中需要多个索引组合使用
    • ref_or_null:
      对于某个字段即需要关联条件,也需要 null 值的情况下,查询优化器会选择这种访问方式
    • ref:
      使用非唯一索引进行数据的查找,或者唯一索引的前缀扫描,返回匹配某个单独值得记录行,ref 还经常出现在 join 中。
    • eq_ref:
      使用唯一索引进行数据查找,对于每一个索引键值,表中只有一条记录匹配,简单来说就是多表连接中使用 primary key 或者 unique index 作为关联条件。
    • const:
      这个表至多有一个匹配行
    • system:
      表只有一行记录(等于系统表),这是const类型的特例
  • possible_keys
    显示可能应用在这张表中的索引,一个或者多个,查询设计到的字段上若存在索引,则列出该索引,但不一定被实际查询使用
  • key
    实际使用的索引,null 表示没有使用索引,查询中若使用了覆盖索引,则该索引和查询的 select 字段重叠
  • key_len
    表示索引中使用的字节数,可以通过 key_len 计算查询中使用的索引长度,在不损失精度的情况下长度越短越好
  • ref
    显示索引的那一列被使用了,如果可能的话,是一个常数
  • rows
    根据表的统计信息以及索引的使用情况,大致估算出找出所需记录需要读取的行数,直接反映 sql 找了多少数据,在完成目的情况下越少越好
  • extra
    包含额外的信息
    • using filesort: 说明 mysql 无法用索引进行排序,只能利用排序算法进行排序,会消耗额外的位置
    • using temporary: 建立临时表来保存中间数据,查询完成之后把临时表删除
    • using index: 表示当前的查询覆盖索引的,直接从索引中读取数据,而不用访问数据表,如果同时出现 using where 表明索引别用来执行索引键值的查找,如果没有,表明索引被用来读取数据,而不是真的查找
    • using where: 使用 where 进行条件过滤
    • using join buffer:使用连接缓存
    • impossible where: where 语句的结果总是 false

官网参考文档

测试表以及数据

  • 数据库
    school 数据库创建 SQL >folded
    1
    CREATE DATABASE IF NOT EXISTS school DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
  • 测试表 >folded
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     # 学生表
    CREATE TABLE `student`(
    `student_id` VARCHAR(20),
    `student_name` VARCHAR(20) NOT NULL DEFAULT '',
    `student_birth` VARCHAR(20) NOT NULL DEFAULT '',
    `student_sex` VARCHAR(10) NOT NULL DEFAULT '',
    PRIMARY KEY(`student_id`)
    ) ENGINE=InnoDB;

    # 课程表
    CREATE TABLE `course`(
    `course_id` VARCHAR(20),
    `course_name` VARCHAR(20) NOT NULL DEFAULT '',
    `teacher_id` VARCHAR(20) NOT NULL,
    PRIMARY KEY(`course_id`)
    ) ENGINE=InnoDB;

    # 教师表
    CREATE TABLE `teacher`(
    `teacher_id` VARCHAR(20),
    `teacher_name` VARCHAR(20) NOT NULL DEFAULT '',
    PRIMARY KEY(`teacher_id`)
    ) ENGINE=InnoDB;

    # 分数表
    CREATE TABLE `score`(
    `student_id` VARCHAR(20),
    `course_id` VARCHAR(20),
    `student_score` BIGINT,
    PRIMARY KEY(`student_id`,`course_id`)
    ) ENGINE=InnoDB;
  • 数据
    测试数据 >folded
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    -- 插入学生表测试数据
    insert into student values('01' , '赵雷' , '1990-01-01' , '男');
    insert into student values('02' , '钱电' , '1990-12-21' , '男');
    insert into student values('03' , '孙风' , '1990-05-20' , '男');
    insert into student values('04' , '李云' , '1990-08-06' , '男');
    insert into student values('05' , '周梅' , '1991-12-01' , '女');
    insert into student values('06' , '吴兰' , '1992-03-01' , '女');
    insert into student values('07' , '郑竹' , '1989-07-01' , '女');
    insert into student values('08' , '王菊' , '1990-01-20' , '女');
    -- 课程表测试数据
    insert into course values('01' , '语文' , '02');
    insert into course values('02' , '数学' , '01');
    insert into course values('03' , '英语' , '03');
    -- 教师表测试数据
    insert into teacher values('01' , '张三');
    insert into teacher values('02' , '李四');
    insert into teacher values('03' , '王五');
    -- 成绩表测试数据
    insert into score values('01' , '01' , 80);
    insert into score values('01' , '02' , 90);
    insert into score values('01' , '03' , 99);
    insert into score values('02' , '01' , 70);
    insert into score values('02' , '02' , 60);
    insert into score values('02' , '03' , 80);
    insert into score values('03' , '01' , 80);
    insert into score values('03' , '02' , 80);
    insert into score values('03' , '03' , 80);
    insert into score values('04' , '01' , 50);
    insert into score values('04' , '02' , 30);
    insert into score values('04' , '03' , 20);
    insert into score values('05' , '01' , 76);
    insert into score values('05' , '02' , 87);
    insert into score values('06' , '01' , 31);
    insert into score values('06' , '03' , 34);
    insert into score values('07' , '02' , 89);
    insert into score values('07' , '03' , 98);

MySQL系列——MySQL的执行计划

https://caolizhi.top/2021-08-mysql-execution-plan/

作者

操先森

发布于

2021-08-19

更新于

2021-11-19

许可协议

评论