您的位置:首页 > 数据库 > MySQL

mysql 性能分析之explain详解

2017-07-18 15:11 471 查看
近期项目做重构,把产线的慢查询日志全部拉出来检查了一遍。发现很多原本是简单的脚本的执行时间居然超过了三秒钟(慢查询设置监控时间为3.0 S)。只好把脚本找到通过mysql 执行计划查查问题到底出在了哪里。不过在这之前就先整理一下mysql 执行计划的相关参数说明。

首先,先知道什么是执行计划(EXPLAIN)。EXPLAIN关键字一般放在SELECT查询语句的前面,用于描述MySQL如何执行查询操作、以及MySQL成功返回结果集需要执行的行数。explain 可以帮助我们分析 select 语句,让我们知道查询效率低下的原因,从而改进我们查询,让查询优化器能够更好的工作。

执行计划的工作方式。MySQL 查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。最终目标是提交 SELECT 语句查找数据行,而不是排除数据行。优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快。如果能够
首先进行最严格的测试,查询就可以执行地更快。附一个简单的案例:

explain select urh.episode_no from plc_user_read_history urh where urh.urh_id = 1000 ;




explain 各项所代表的信息:
1.id:
MySQL Query Optimizer 选定的执行计划中查询的序列号。表示查询中执行 select 子句或操作表的顺序,id 值越大优先级越高,越先被执行。id 相同,
执行顺序由上至下。
2.select_type:
查询类型,这个非常重要,但是也很难搞。

2.1 simple 它表示简单的select,没有union和子查询。

2.2 primary 最外面的select,在有子查询的语句中,最外面的select查询就是primary。

2.3 union UNION 中的第二个或随后的 select 查询,不 依赖于外部查询的结果集.现执行一条语句,explain

explain
select ui.account from u_user_info ui limit 2
union
select ci.account  from c_client_info ci  limit 2



2.4 dependent union UNION中的第二个或后面的SELECT语句,取决于外面的查询

2.5 union result UNION的结果 参考上例。

上面这些是比较常见的,还有一些不常见的。

2.6 subquery 子查询中的第一个 select 查询,不依赖于外 部查询的结果集。

2.7 dependent subquery 子查询中的第一个 select 查询,依赖于外部 查询的结果集

2.8 deverived 用于 from 子句里有子查询的情况。 MySQL 会 递归执行这些子查询, 把结果放在临时表里。

2.9 uncacheable subquery 结果集不能被缓存的子查询,必须重新为外 层查询的每一行进行评估。

2.10 uncacheable union 中的第二个或随后的 select 查询,属 于不可缓存的子查询 。

3.table :

这个比较简单,输出行所引用的表。

[b]4.type :[/b]

[b]连接类型,参数类型比较多,这里从最佳到最差逐个介绍。(这项很重要)[/b]

4.1 system 表中数据仅有一行(==系统表)。是const 连接类型的一个特例,平时少见可以忽略

4.2 const 表中至多只有一行数据匹配。一般是常量匹配主键primary key 或者 unique 索引。当表中只有一行时,则为system 。可以简单理解为 const 是最优的。

4.3 eq_ref mysql 官方手册解释是:“对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型。它用在一个索引的所有部分被联接使用并且索引是primary key 或者 unique ”。eq_ref 可以用于使用 “=” 比较带索引的列。如下:

select ui.account from u_user_info ui ,c_client_info ci where ci.account = ui.account ;



4.4 ref 连接不能基于关键字选择单个行,可能查找 到多个符合条件的行(关联的键不是primary key 或者 unique )。 叫做 ref 是因为索引要 跟某个参考值相比较。这个参考值或者是一 个常数,或者是来自一个表里的多表查询的 结果值。如果能确定所用的键仅仅匹配少量行,该连接类型性能会更好。

4.5 ref_or_null 连接类型如同ref ,但是添加了mysql 可以专门搜索包含null值的行,mysql必须在初次查找的结果中找出null 记录所在的条目,然后进行二次查找。

以上五种是比较理想的索引使用情况,接下来的遇到之后都要考虑脚本是否能够优化了。

4.6 index_merge 该连接表示使用了索引合并优化方法。这时候 key(稍后介绍) 列包含了使用的索引清单,key_len 包含了使用的索引的嘴馋个关键元素。

4.7 unique_subquery 在某些 IN 查询中使用此种类型,而不是常规的 ref:value IN (SELECT primary_key FROM single_table WHERE some_expr)。

4.8 index_subquery 在 某 些 IN 查 询 中 使 用 此 种 类 型 , 与 unique_subquery 类似,但是查询的是非唯一 性索引: value IN (SELECT key_column FROM single_table WHERE some_expr)

4.9 range 只检索给定范围的行,使用一个索引来选择 行。key 列显示使用了哪个索引。当使用=、 <>、>、>=、<、<=、IS NULL、<=>、BETWEEN 或者 IN 操作符,用常量比较关键字列时,可以使用 range 。

4.10 index 与ALL 相同都是全表扫描,但是只扫描索引树,按照索引次序扫描,而不是行。索引文件一般比数据文件小,所以数度一般快于all 。 换句话说虽然跟all一样读全表,但是index 是从索引中读取的而all是从硬盘中读取的。可以避免排序,但是查询开销依然很大。使用场景,当查询只使用作为单一索引一部分的列时,mysql可以采用该类型。

4.11 all 最坏的情况,全表扫描。对于每个来自于前表的行集合,进行完整的表扫描,性能很差。一般通过增加索引从而避免all。

5. possible_key

指出MySQL 能在该表中使用哪些索引有助于 查询。如果为空,说明没有可用的索引。

6.key

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

7.key_len

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

8.ref

小时索引的哪一列被使用来筛选数据。

9.rows

MYSQL 认为必须检查的用来返回请求数据的行数。通常作为检测脚本性能优化结果的参考数据。

10.extra


该列包含mysql解决查询的详细信息(重要)

10.1 Distinct mysql 发现第一个匹配行后,停止搜索。(没见过)。

10.2 not exists 不存在可用的索引

10.3 range checked for each record 没找到合适的索引。

10.4 Using filsort . mysql手册官方解释为“MYSQL需要额外的一次传递,以找出如何安排顺序检索行。通过根据连接类型浏览所有行,并为所有匹配where 子句的行保存排序关键字和行的指针来完成排序。然后关键字被排序,并按照排序顺序检索行。”
官方解释比较拗口,简单来说就是Mysql会对结果使用一个外部索引排序,而不是从表里按照索引次序读到相关内容。可能在内存或者磁盘上进行排序。Mysql中无法利用索引完成的排序操作称为“文件排序”。出现这种情况意味着mysql 根本不能用任何索引,效率会非常低,要特别留意。

explain
select * from n_novel_info ni , n_novel_chapter nc where ni.ni_id = 10 and nc.is_free = 'Y' and ni.ni_id = nc.ni_id order by nc.nc_id desc ;



10.5 using index . 只是用索引树中的信息,而不需要进一步搜索读取实际的行为来检索表中的信息。简单说就是说明是否使用了索引。

10.6 using temporary
. 表示mysql 在对查询结果排序时使用临时表,常见于排序order by 和分组查询 group by 。 同using filesort 一样一旦出现这种情况,意味着脚本需要优化。这种情况下mysql 也不能使用索引。案例:

explain select ui.account a from u_user_info ui union select ci.account a from c_client_info ci order by a limit 10




mysql表关联的算法是Nest Loop Join , 是通过驱动表的结果集作为循环的基础数据,然后逐条通过该结果集中的数据作为过滤条件到下一个表中查询数据。然后合并结果。explain 结果中,第一行出现的表就是驱动表,所以该查询的驱动表为ui
如执行计划所示。

驱动表可以直接排序,而对于非驱动表的字段排序,需要对循环查询的合并结果(存放于临时表)进行排序。

补充:

关于驱动表的定义:

1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表];2)未指定联接条件时,行数少的表为[驱动表](Important!)。永远用小结果集驱动大结果集

特别的:当不确定是用哪种类型的join时,让mysql优化器自动去判断,我们只需写select * from t1,t2 where t1.field = t2.field

10.7 using where

where 子句用于限制哪一个行匹配下一个表或者发送到客户。除非专门从表中索取或者检查所有行,如果Extra 值不是using where 并且表连接类型为all 或者 index ,查询可能会有错误。(尽管using where 比较常见,但是这个还没见过这种情况,只是不少大牛都会提示这点。using
where不是很重要,大部分语句都有where条件,而type 为all 或者index 只是说明检索数据比较多,并不能说明错误。)

10.8Using sort_union(...), Using union(...),Using intersect(...)

这些函数说明如何为index_merge联接类型合并索引扫描

10.9 Using index for group-by

类似于访问表的Using index方式,Using index for group-by表示MySQL发现了一个索引,可以用来查询GROUP BY或DISTINCT查询的所有列,而不要额外搜索硬盘访问实际的表。并且,按最有效的方式使用索引,以便对于每个组,只读取少量索引条目。

最后,MySql 中的 explain 语法可以帮助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。当然,在大规模数据量时,索引的建立和维护的代价也是很高的,往往需要较长的时间和较大的空间,如果在不同的列组合上建立索引,空间的开销会更大。因此索引最好设置在需要经常查询的字段中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息