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

栏目: IT技术 · 发布时间: 6年前

内容简介:产品:首页为什么加载这么慢开发:数据量太大了,没办法

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

产品:首页为什么加载这么慢

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

产品:不行,必须优化

开发: ....

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

本文目的

1. 帮助大家认识 explain, 遇到上述问题的时候可以到此来查阅执行计划中每个字段的意思

2.  能根据慢查询的执行计划快速找到问题所在

3. 提供常见的问题原因以及解决方案

explain能干嘛

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

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

从图中可以看出 , 我们的 sql 在查询的时候主要需要经历以下步骤 :

1. 与 mysql 建立连接

2. 查询缓存是否存在 , 如果有则直接返回结果

3. 解析器 , 主要是对 sql 进行解析

4. 查询优化器 , 主要对 sql 进行各种优化 , 包括重写查询、决定表的读取顺序以及选择合适的索引等等。。并产生执行计划

5. 去存储引擎查询结果

而我们使用 explain 即是去查询优化器查询执行计划。

explain字段解释

看一条简单的执行计划 :

explain select * from t_user where id = 1;

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

1

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

3

table

涉及到的表

4

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

全表扫描

5

possible_keys

表示可能用到的索引

6

key

表示最终用到的 key

7

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

8

rows

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

9

filtered

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

10

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 点其实都可以通过优化索引 来达到目的 , 而要想让我们建的索引达到最优 , 则需要依据一个原则 : 三星索引原则 , 简单描述就是 :

☆ : 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, 出现 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 的子句不一样时

如下 Sql:

explain select * from t_user group by group_id order by `name`;

这种情况只能尽量使用同一个字段来分组和排序了,否则无法避免

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

如下 sql:

explain select DISTINCT(`name`) from t_user order by age;

这种情况有时候无法避免,只能尽量将 distinct 的字段和 order by 的字段使用相同的索引。

还有会出现临时表的情况有 : from 中的子查询、 union ,这里就不一一举例了。

·END·

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

微信号:好享家技术团队

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Zero to One

Zero to One

Peter Thiel、Blake Masters / Crown Business / 2014-9-16 / USD 27.00

“This book delivers completely new and refreshing ideas on how to create value in the world.” - Mark Zuckerberg, CEO of Facebook “Peter Thiel has built multiple breakthrough companies, and ......一起来看看 《Zero to One》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具