美文网首页
看完这篇文章,99%的人都会使用Mysql Explain工具

看完这篇文章,99%的人都会使用Mysql Explain工具

作者: Sunny捏 | 来源:发表于2020-09-11 09:07 被阅读0次

    Explain工具介绍

    EXPLAIN是MySQl必不可少的一个分析工具,主要用来测试sql语句的性能及对sql语句的优化,或者说模拟优化器执行SQL语句。

    注意:本文基于mysql5.7进行操作,各个版本的mysql使用Explan会有微小的差异

    前期掌握点:

    什么是mysql的二级索引

    mysql中每个表都有一个聚簇索引(clustered index ),除此之外的表上的每个非聚簇索引都是二级索引,又叫辅助索引(secondary indexes)。
    以InnoDB来说,每个InnoDB表具有一个特殊的索引称为聚集索引。如果您的表上定义有主键,该主键索引是聚集索引。如果你不定义为您的表的主键 时,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚集索引。如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引。

    示例表

    -- ----------------------------
    -- Table structure for order
    -- ----------------------------
    DROP TABLE IF EXISTS `order`;
    CREATE TABLE `order`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
      PRIMARY KEY (`id`) USING BTREE,
      INDEX `idx_name`(`name`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of order
    -- ----------------------------
    INSERT INTO `order` VALUES (2, '华为电脑');
    INSERT INTO `order` VALUES (1, '小米电脑');
    
    -- ----------------------------
    -- Table structure for user
    -- ----------------------------
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user`  (
      `id` int(11) NOT NULL,
      `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `age` int(11) NULL DEFAULT NULL,
      `create_time` datetime(0) NULL DEFAULT NULL,
      `update_time` datetime(0) NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO `user` VALUES (1, '黎明1', 25, '2020-09-09 22:49:08', '2020-09-09 22:49:10');
    INSERT INTO `user` VALUES (2, '黎明2', 25, '2020-09-09 22:49:20', '2020-09-09 22:49:22');
    INSERT INTO `user` VALUES (3, '黎明3', 25, '2020-09-09 22:49:29', '2020-09-09 22:49:30');
    
    -- ----------------------------
    -- Table structure for user_order
    -- ----------------------------
    DROP TABLE IF EXISTS `user_order`;
    CREATE TABLE `user_order`  (
      `id` int(11) NOT NULL,
      `oid` int(11) NOT NULL,
      `uid` int(11) NOT NULL,
      `ramark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE,
      INDEX `idx_user_order_id`(`oid`, `uid`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
    -- ----------------------------
    -- Records of user_order
    -- ----------------------------
    INSERT INTO `user_order` VALUES (1, 1, 1, NULL);
    INSERT INTO `user_order` VALUES (2, 1, 2, NULL);
    INSERT INTO `user_order` VALUES (3, 2, 1, NULL);
    

    接下来开始对explan结果集列详细解释

    EXPLAIN select * from `order` where id = 2
    

    1.id列

    id列的编号是 select 的序列号,有几个select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。 id列越大执行优先级越高,id相同则从上往下执行,id为NULL最后执行。

    2.select_type列

    select_type:表示对应行是简单查询还是复杂查询

    • simple:简单查询。查询不包含子查询和union
    • primary:复杂查询中最外层的 select
    • subquery:包含在 select 中的子查询(不在 from 子句中)
    • derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义)
    • union:在 union 中的第二个和随后的所有select

    下面这个例子主要来了解(simple)查询类型

    EXPLAIN select * from `order` where id = 1
    

    下面这个例子主要来了解(primary、subquery、derived)几种类型

    set session optimizer_switch='derived_merge=off'; #关闭mysql5.7新特性对衍生表的合并优化
    explain select (select 1 from user where id = 1) from (select * from `order` where id = 1) ud;
    

    其实在这里我们可以看到id列,id值最大先执行,那么肯定是先执行是派生表查询,接着第二步执行子查询,最后是最外层的select查询

    下面这个例子主要来了解(union)查询类型

    explain select * from user union all select * from `order`
    
    set session optimizer_switch='derived_merge=on'; #还原默认配置
    

    3.table列

    这一列表示 explain 的一行正在访问哪个表。
    当 from 子句中有子查询时,table列是 <derivenN> 格式,表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查询。
    当有 union 时,UNION RESULT 的 table 列的值为<union1,2>,1和2表示参与 union 的 select 行id。

    具体例子可参考,上面第二张图的table列

    4.type列

    这一列表示关联类型或访问类型,即MySQL决定如何查找表中的行,查找数据行记录的大概范围。
    依次从最优到最差分别为:system > const > eq_ref > ref > range > index > ALL (system效率最高,All效率最低)
    一般来说,得保证查询达到range级别,最好达到ref

    NULL:
    mysql能够在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。
    例如:在索引列中选取最小值,可以单独查找索引树来完成,不需要在执行时访问表(简单来说,表的数据是以b+树格式存储的,而且叶子节点的data值是有序的,那么通过Min函数,可以直接再索引里面找到最小的值,无需查询表了,所以这里的type才会为null,所以这种性能是非常高的)

    explain select min(id) from user
    

    const, system:
    mysql能对查询的某部分进行优化并将其转化成一个常量(可以看show warnings 的结果)。
    用于 primary key (主键索引)或 unique key (唯一索引) 的所有列与常数比较时,所以表最多有一个匹配行,读取1次,速度比较快。system是const的特例,表里只有一条元组匹配时为system

    --执行如下语句,结果要和图中一样的话, #得先关闭mysql5.7新特性对衍生表的合并优化 set session optimizer_switch='derived_merge=off';  还原配置:set session optimizer_switch='derived_merge=on';
    explain select * from (select * from `order` where id = 1) tmp;
    

    关于下图中,给出优化的sql,显示查询的是dual表,这个是mysql的自带的空表,像我们上面的sql,mysql会直接将数据解析到空表中,查询返回出去,所以效率才会高

    eq_ref:
    primary key (主键索引) 或 unique key (唯一索引) 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录。这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种 type

    explain select * from user_order left join `order` on user_order.oid = `order`.id
    

    ref:
    相比 eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行记录。

    1. 简单 select 查询,name是普通索引(非唯一索引)
    explain select * from `order` where name = '小米电脑';
    

    2.关联表查询,idx_user_order_id是oid和uid的联合索引,这里使用到了user_order的左边前缀oid部分。(这里底层是使用了覆盖索引查询,因为我的连表查询条件中,两个id都是被索引关联着,这样的话,可以直接通过辅助索引就能定位到记录,而不需要再通过辅助索引的id再去主键索引中查询其他的记录了)

    explain select oid from `order` left join user_order on `order`.id = user_order.oid
    

    range:
    范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行记录。

    explain select * from user where id > 1;
    

    index:
    扫描全索引就能拿到结果,一般是扫描某个二级索引,这种扫描不会从索引树根节点开始快速查找,而是直接对二级索引的叶子节点遍历和扫描,速度还是比较慢的,这种查询一般未使用覆盖索引,二级索引一般比较小,所以这种通常比ALL快一些。

    explain select * from `order`;
    

    ALL:即全表扫描,扫描你的聚簇索引(什么是聚簇索引文章开头有解释)的所有叶子节点。通常情况下这需要增加索引来进行优化了。

    explain select * from user;
    

    5.possible_keys列

    这一列显示查询可能使用哪些索引来查找。

    6.key列

    这一列显示mysql实际采用哪个索引来优化对该表的访问。

    7.key_len列

    这一列显示了mysql在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。 举例来说,film_actor的联合索引 idx_user_order_id 由 oid 和 uid 两个int列组成,并且每个int是4字节。通 过结果中的key_len=4可推断出查询使用了第一个列:uid列来执行索引查找。

    explain select * from user_order where oid = 2;
    

    8.ref列

    这一列显示了在key列记录的索引中,表查找值所用到的列或常量

    9.rows列

    这一列是mysql估计要读取并检测的行数,注意这个不是结果集里的行数。

    10.Extra列

    这一列展示的是额外信息。常见的重要值如下:

    • Using index:使用覆盖索引
    • Using where:使用 where 语句来处理结果,并且查询的列未被索引覆盖(简单理解为where条件查询的字段没有添加索引)
    • Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围;
    • Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索 引来优化。
    • Using filesort:将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一 般也是要考虑使用索引来优化的。
    • Select tables optimized away:使用某些聚合函数(比如 max、min)来访问存在索引的某个字段

    相关文章

      网友评论

          本文标题:看完这篇文章,99%的人都会使用Mysql Explain工具

          本文链接:https://www.haomeiwen.com/subject/zcymektx.html