SQL优化-使用explain分析SQL执行计划

有时候咱们会遇到这样的事情:项目上线了,一切顺利,就等产品验收完毕回家睡觉了,可是产品忽然来找你了:mysql

产品:首页为何加载这么慢算法

开发:数据量太大了,没办法sql

产品:不行,必须优化后端

开发:....缓存

这种状况,很大几率是sql出现慢查询了,此时咱们就须要把查询sql拉出来优化优化了,那咱们就须要使用到本文要说的explain命令了。bash

本文目的

  1. 帮助你们认识explain,遇到上述问题的时候能够到此来查阅执行计划中每一个字段的意思
  2. 能根据慢查询的执行计划快速找到问题所在
  3. 提供常见的问题缘由以及解决方案

explain能干吗

在了解explain以前,不妨先看下mysql服务大体的逻辑架构图,以对其有一个总体的认识服务器

explain

从图中能够看出,咱们的sql在查询的时候主要须要经历如下步骤:架构

  1. 与mysql创建链接
  2. 查询缓存是否存在,若是有则直接返回结果
  3. 解析器,主要是对sql进行解析
  4. 查询优化器,主要对sql进行各类优化,包括重写查询、决定表的读取顺序以及选择合适的索引等等。。并产生执行计划
  5. 去存储引擎查询结果

而咱们使用explain便是去查询优化器查询执行计划框架

explain字段解释

看一条简单的执行计划性能

explain select * from t_user where id = 1;
复制代码

执行计划1

咱们能够看到,一个执行计划会展现12个相关的字段,下面咱们对主要字段以及这些字段常见的值进行解释:

id

含义:是一组数字,表示的是查询中执行select子句或者是操做表的顺序

规则:

  1. id不相同的,id值越大越先执行
  2. id相同的,从上到下顺序执行

select_type

常见的值以及描述以下

描述
SIMPLE 简单的SELECT语句(不包括UNION操做或子查询操做)
PRIMARY 查询中最外层的SELECT(如两表作UNION或者存在子查询的外层的表操做为PRIMARY,内层的操做为UNION)
UNION UNION操做中,查询中处于内层的SELECT,即被union的SELECT
SUBQUERY 子查询中的SELECT
DERIVED 表示包含在 From 子句中的 Select 查询
UNION RESULT union的结果,此时id为NULL

table

涉及的表

type(重要)

这列很重要,显示了链接使用哪一种类型,有无使用索引, 常见的值从最好到最差以下 system > const > eq_ref > ref > range > index > all

各值的描述以下

描述
system 表只有一行,MyISAM引擎全部
const 常量链接,表最多只有一行匹配,一般用于主键或者惟一索引比较时,如:
select * from t_user where id = 1;
eq_ref 表关联查询时,对于前表的每一行,后表只有一行与之匹配。
(1) join查询
(2) 命中主键或者非空惟一索引
ref 只使用了索引的最左前缀或者使用的索引是非惟一索引、非主键索引
range between,in,>等都是典型的范围(range)查询
index 须要扫描索引上的所有数据,如:
select count(*) from t_user;
all 全表扫描

possible_keys

表示可能用到的索引

key

表示最终用到的key

ref

显示索引的哪一列被使用了,有时候会是一个常量:表示哪些列或常量被用于查找索引列上的值

rows

估算出结果集行数,表示MySQL根据表统计信息及索引选用状况,估算的找到所需的记录所须要读取的行数, 原则上 rows 越少越好。

filtered

查询结果的行数占上面rows的百分比

Extra(重要)

这一列也很重要,主要展现额外的信息说明,可以给出让咱们深刻理解执行计划进一步的细节信息

常见的值及描述以下

描述
Using filesort 当order by 没法利用索引完成排序时,优化器不得不选择合适的算法从内存或者磁盘进行排序
Using temporary 使用了临时表
Using index select后面的查询字段在索引中就能够取到,无需再回表了,即所谓的覆盖索引,这种查询性能很好
Using index condition mysql5.6以后引入了ICP(索引条件下推)
Using where Mysql 服务器在存储引擎检索行后再进行过滤

优化原则

一般有如下几种优化原则:

  1. 让主要查询语句使用到合适的索引,type出现ALL(全表扫描)需格外注意,同时创建合适的索引以减小possible_keys的数量

  2. type最好能达到ref级别

  3. Extra列出现Using temporary、Using filesort(文件排序)务必去除

优化思路

针对上面提到的几点优化原则,提供以下的优化思路

针对优化原则1,2

上述1,2点其实均可以经过优化索引来达到目的,而要想让咱们建的索引达到最优,则须要依据一个原则: 三星索引原则

简单描述就是

☆: where后条件匹配的索引列越多扫描的数据将越少

好比组合索引(a,b,c),最好在where后面能同时用到索引上的a,b,c这三列

☆: 避免再次排序

简单来讲,就是排序字段尽可能使用索引字段,由于索引默认是排好序的,使用索引字段排序能够避免再次排序

☆: 索引行包含查询语句中全部的列,即覆盖索引

基于这一点,咱们应该少用select*来查询,以增长覆盖索引的可能性

若是你的索引能集齐上述三颗星,则说明你的索引是最优的索引!

针对优化原则3

咱们建立以下表,并插入一些数据

用户表

CREATE TABLE `t_user`  (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `group_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1240277101395107842 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
复制代码

分组表

CREATE TABLE `t_group`  (
  `id` bigint(20) NOT NULL,
  `group_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
复制代码

Using filesort

  1. order by 的字段不在where条件中

    下面这条sql会出现Using filesort

    select * from t_user where group_id = 2 and age = 32 order by name;
    复制代码

    执行计划

    可是下面这条sql不会

    select * from t_user where group_id = 2 and age = 32 order by group_id ;
    复制代码

    执行计划

  2. 组合索引跨列

    举例:给t_user表建立索引(name,age,group_id)

    下面这条sql排序会出现Using filesort

    select * from t_user where name= '李A' order by group_id;
    复制代码

    执行计划

    可是下面这条就不会

    select * from t_user where name = '李A' order by age;
    复制代码

    执行计划

    由于第一条查询order by跳过了age,直接使用了group_id;删除索引(name,age,group_id);

  3. 因为group by第一步默认进行了排序,因此当group by 的字段知足上述条件是,也会出现Using filesort,能够在group by后面加上order by null取消排序

Using temporary

临时表的出现对性能影响是很大的,主要会出如今如下状况中

  1. 分组字段不在where条件后面,而且group by字段不是最终使用到的索引,缘由有点相似于上面的Using filesort

    下面这条sql会出现Using temporary

    select * from t_user where group_id = 2 and name= '李A' group by age;
    复制代码

    执行计划

    可是下面这条sql不会

    select * from t_user where name = '李A' and age = 21 group by age;
    复制代码

    结论: where哪些字段,就group by 哪些字段

  2. 表链接中,order by的列不是驱动表中的

    以下sql是会建立临时表的

    explain select * from t_user t1 left join t_group t2 on t1.group_id = t2.id order by t2.id;
    复制代码

    执行计划

    由于t1和t2链接的时候,t1是驱动表,可是排序使用了被驱动表t2中的字段。改成t1的字段排序就不会出现临时表了,这里就不举例了。

    结论: 链接查询的时候,排序字段使用驱动表的字段

  3. order by和group by的子句不同时

    explain select * from t_user group by group_id order by `name`;
    复制代码

    执行计划

    这种状况只能尽可能使用同一个字段来分组和排序了,不然没法避免

  4. distinct查询而且加上order by时

    explain select DISTINCT(`name`) from t_user order by age;
    复制代码

    执行计划

    这种状况有时候没法避免,只能尽可能将distinct的字段和order by的字段使用相同的索引。还有会出现临时表的状况有: from 中的子查询、union,这里就不一一举例了。

总结

sql优化已是咱们后端开发的内化技能之一了,在学习框架,设计思想的同时,不要忘记打牢基础,但愿各位可以有所收获。

相关文章
相关标签/搜索