MYSQL执行计划查看详解

 

1、使用explain语句去查看分析结果

如explain select * from  sm_goods; 会出现:id selecttype table type possible_keys key key_len ref rows extra各列。如下图

mysql中如何查看执行计划 mysql查看执行计划命令_mysql中如何查看执行计划

解释:

type=const表示通过索引一次就找到了;

key=primary的话,表示使用了主键;

type=all,表示为全表扫描;

key=null表示没用到索引。

type=ref,表示是多个匹配行,在联合查询中,一般为REF。

 

EXPLAIN列的解释

table

显示这一行的数据是关于哪张表的

type

这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL

说明:不同连接类型的解释(按照效率高低的顺序排序)

system:表只有一行:system表。这是const连接类型的特殊情况。

const :表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待。

eq_ref:在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用。

ref:这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。

range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况。

index:这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)。

ALL:这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免。

possible_keys

显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句

key

实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引

key_len

使用的索引的长度。在不损失精确性的情况下,长度越短越好

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数

rows

MYSQL认为必须检查的用来返回请求数据的行数

Extra

关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢

说明:extra列返回的描述的意义

Distinct :一旦mysql找到了与行相联合匹配的行,就不再搜索了。

Not exists :mysql优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了。

Range checked for each Record(index map:#) :没有找到理想的索引,因此对从前面表中来的每一个行组合,mysql检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。

Using filesort :看到这个的时候,查询就需要优化了。mysql需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。

Using index :列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。

Using temporary :看到这个的时候,查询需要优化了。这里,mysql需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上。

Where used :使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题

 

 

 

 

2.MYSQL中的组合索引

假设表有id,key1,key2,key3,把三者形成一个组合索引,则如:

复制代码 代码如下:

 

MYSQL索引类型

B-Tree 索引的特点

 

B-tree 索引可以用于使用 =, >, >=, <, <= 或者 BETWEEN 运算符的列比较。如果 LIKE 的参数是一个没有以通配符起始的常量字符串的话也可以使用这种索引。

有时,即使有索引可以使用,MySQL 也不使用任何索引。发生这种情况的场景之一就是优化器估算出使用该索引将要求 MySql 去访问这张表的绝大部分记录。这种情况下,一个表扫描可能更快,因为它要求更少量的查询。但是,如果这样的一个查询使用了 LIMIT 来检索只是少量的记录时,MySql 还是会使用索引,因为它能够更快地找到这点记录并将其返回。

 

Hash 索引的特点

 

Hash 索引只能够用于使用 = 或者 <=> 运算符的相等比较(但是速度更快)。Hash 索引不能够用于诸如 < 等用于查找一个范围值的比较运算符。依赖于这种单值查找的系统被称为 “键-值存储”;对于这种系统,尽可能地使用 hash 索引。

优化器不能够使用 hash 索引来加速 ORDER BY 操作。这种类型的索引不能够用于按照顺序查找下一个条目。

MySql 无法使用 hash 索引估计两个值之间有多少行(这种情况由范围优化器来决定使用哪个索引)。如果你将一张 MyISAM 或 InnoDB 表转换成一个 hash 索引的内存表时,一些查询可能会受此影响。

查找某行记录必须进行全键匹配。而 B-tree 索引,任何该键的左前缀都可用以查找记录。

 

最左前缀原则

 

通过实例理解单列索引、多列索引以及最左前缀原则

实例:现在我们想查出满足以下条件的用户id:

SELECT  uid  FROM  people  WHERE  lname =’Liu’ AND  fname=’Zhiqun’ AND age=26

因为我们不想扫描整表,故考虑用索引。

1、单列索引:

ALTER TABLE people ADD INDEX lname (lname);

将lname列建索引,这样就把范围限制在lname=’Liu’的结果集1上,之后扫描结果集1,产生满足fname=’Zhiqun’的结果集2,再扫描结果集2,找到 age=26的结果集3,即最终结果。

 

2、多列索引:

ALTER TABLE people ADD INDEX lname_fname_age (lame,fname,age);

为了提高搜索效率,我们需要考虑运用多列索引,由于索引文件以B-Tree格式保存,所以我们不用扫描任何记录,即可得到最终结果。

 

注:在mysql中执行查询时,只能使用一个索引,如果我们在lname,fname,age上分别建索引,执行查询时,只能使用一个索引,mysql会选择一个最严格(获得结果集记录数最少)的索引。

 

3、最左前缀:顾名思义,就是最左优先,上例中我们创建了lname_fname_age多列索引,相当于创建了(lname)单列索引,(lname,fname)组合索引以及(lname,fname,age)组合索引。

注:在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。

 

场景描述:多列字段做索引,state/city/zipCode,想要索引生效的话,只能使用如下的组合

state/city/zipCode

state/city

state

其他方式(如city,city/zipCode),则索引不会生效,这是为什么呢?

因为索引的最左前缀原则,所以就是先要看第一列,在第一列满足的条件下再看左边第二列,以此类推。

方便记忆:你可以认为联合索引是闯关游戏的设计

例如你这个联合索引是state/city/zipCode

那么state就是第一关 city是第二关, zipCode就是第三关

你必须匹配了第一关,才能匹配第二关,匹配了第一关和第二关,才能匹配第三关

你不能直接到第二关的

 

 

 

 

 

 

 

SQL语句编写的注意点:

在一些where子句中,即使某些列存在索引,但是由于编写了劣质的SQL,系统在运行该SQL语句时也不能使用该索引,而同样使用全表扫描,这就造成了响应速度的极大降低。

 

1、IS NULL 与 IS NOT NULL

不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多个列,这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。

 

任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的。

 

2、联接列

  对于有联接的列,即使最后的联接值为一个静态值,优化器是不会使用索引的。我们一起来看一个例子,假定有一个职工表(employee),对于一个职工的姓和名分成两列存放(FIRST_NAME和LAST_NAME),现在要查询一个叫比尔.克林顿(Bill Cliton)的职工。

下面是一个采用联接查询的SQL语句:

select * from employss where first_name||''||last_name ='Beill Cliton';

 

上面这条语句完全可以查询出是否有Bill Cliton这个员工,但是这里需要注意,系统优化器对基于last_name创建的索引没有使用。

 

*** where first_name ='Beill' and last_name ='Cliton';但是这种写法的数据库会使用last_name的索引

 

如果通配符(%)在搜寻词首出现,带通配符(%)的like语句将不会使用该字段上的索引

例如:select * from employee where last_name like '%cliton%';将不会使用last_name 的索引

 

然而当通配符出现在字符串其他位置时,优化器就能利用索引。在下面的查询中索引得到了使用:select  *  from  employee  where  last_name  like  'c%';

 

3、Order by语句

任何在Order by语句中的非索引项 或者存在有计算表达式都将降低查询速度。

仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。

 

 

4、NOT

   在查询时经常在where子句使用一些逻辑表达式,如大于、小于、等于以及不等于等等,也可以使用and(与)、or(或)以及not(非)。NOT可用来对任何逻辑运算符号取反。下面是一个NOT子句的例子:

... where not (status ='VALID')

如果要使用NOT,则应在取反的短语前面加上括号,并在短语前面加上NOT运算符。NOT运算符包含在另外一个逻辑运算符中,这就是不等于(<>)运算符。换句话说,即使不在查询where子句中显式地加入NOT词,NOT仍在运算符中,见下例:

... where status <>'INVALID';

对这中类型的查询,可以改写为不使用NOT:

select * from employee where salary<3000 or salary>3000;

虽然这两种查询的结果一样,但是第二种查询方案会比第一种查询方案更快些。第二种查询允许数据库对salary列使用索引,而第一种查询则不能使用索引。

 

5、避免使用 * ,因为数据库会把*转换为所有的列名,这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间

 

6、减少数据库的访问次数

 

7、在对业务无影响的情况下删除重复数据

 

8、用Where子句替换HAVING子句:

 

9、避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销. on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后,因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的,where也应该比having快点的,因为它过滤数据后 才进行计算,

 

10、通过内部函数提高SQL效率.:

复杂的SQL往往牺牲了执行效率. 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的

 

11、使用表的别名(Alias):

当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.

 

12、用EXISTS替代IN、用NOT EXISTS替代NOT IN:

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率. 在子查询中,NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS。

(高效)SELECT * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  EXISTS (SELECT ‘X'  FROM DEPT  WHERE  DEPT.DEPTNO = EMP.DEPTNO  AND  LOC = ‘MELB')

(低效)SELECT  * FROM  EMP (基础表)  WHERE  EMPNO > 0  AND  DEPTNO IN(SELECT DEPTNO  FROM  DEPT  WHERE  LOC = ‘MELB')

 

13、避免在索引列上使用NOT ,

 

14、避免在索引列上使用计算.用>=替代>

WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.

举例:

低效:

SELECT … FROM  DEPT  WHERE SAL * 12 > 25000;

高效:

SELECT … FROM DEPT WHERE SAL > 25000/12;

 

15、用UNION替换OR (适用于索引列)

通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引.

高效:

 

SELECT LOC_ID , LOC_DESC , REGION

FROM LOCATION

WHERE LOC_ID = 10

UNION

SELECT LOC_ID , LOC_DESC , REGION

FROM LOCATION

WHERE REGION = “MELBOURNE”

低效:

 

SELECT LOC_ID , LOC_DESC , REGION

FROM LOCATION

WHERE LOC_ID = 10 OR REGION = “MELBOURNE”

 

16、用IN来替换OR

低效:

SELECT…. FROM LOCATION WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30

高效:

SELECT… FROM LOCATION WHERE LOC_IN  IN (10,20,30);

 

17、用WHERE替代ORDER BY:

ORDER BY 子句只在两种严格的条件下使用索引.

ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.

ORDER BY中所有的列必须定义为非空.

WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.