性能分析
要说sql有问题,需要拿出证据,因此需要性能分析
Mysql查询优化器(Mysql Query Optimizer)
- Mysql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认为最优的执行计划(它认为最优的数据检索方式,不见得是DBA认为是最优的,这部分最耗费时间,如果知道mysql底层是如何执行一条sql,可以帮助我们更好地优化sql)
- 当客户端向MySQL请求一条Query,命令解析器模块完成请求分类,区别出是 SELECT 转发给MysQL Query Optimizer,MysQL Query Optimizer首先会对整条Query进行优化,处理掉一些常量表达式的预算,直接换算成常量值。并对Query中的查询条件进行简化和转换,如去掉一些无用或显而易见的条件、进行结构调整。然后分析 Query 中的 Hint 信息(如果有),看显示Hint信息是否可以完全确定该Query 的执行计划。如果没有 Hint 或Hint 信息还不足以完全确定执行计划,则会读取所涉及对象的统计信息,根据 Query 进行写相应的计算分析,然后再得出最后的执行计划。
Mysql常见瓶颈
- CPU: CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候
- IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
- 服务器硬件的性能瓶颈(机器性能本来就弱): top,free,iostat和vmstat来查看系统的性能状态
EXPLAIN简介
EXPLAIN是什么?
EXPLAIN:SQL的执行计划,使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的
,进而分析查询语句或是表结构的性能瓶颈。
EXPLAIN怎么使用?
语法:explain
关键字 + 所写的SQL
。
【表格形式查看】
explain select * from tbl_emp;
【列形式查看】
mysql> explain select * from pms_category \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: pms_category
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1425
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
EXPLAIN能干嘛?
通过上面的表格可以查看以下信息:
id列 了解表的读取顺序
值有以下三种情况:
id
相同,执行顺序由上至下。
id
不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行。
id
相同和不同 同时存在。永远是id大的优先级最高,id相等的时候从上到下顺序执行。
总结:id相同上下顺序走,不同id大的先走
select_type列 数据读取操作的操作类型。
select_type
:数据查询的类型,主要是用于区别,普通查询、联合查询、子查询等复杂查询。
【常见值】
SIMPLE
:简单的SELECT
查询,查询中不包含子查询或者UNION
。PRIMARY
:查询中如果包含任何复杂的子部分,最外层查询则被标记为PRIMARY
。SUBQUERY
:在SELECT
或者WHERE
子句中包含了子查询。DERIVED
:在FROM
子句中包含的子查询被标记为DERIVED(衍生)
,MySQL会递归执行这些子查询,把结果放在临时表中。UNION
:如果第二个SELECT
出现在UNION
之后,则被标记为UNION
;若UNION
包含在FROM
子句的子查询中,外层SELECT
将被标记为DERIVED
。UNION RESULT
:从UNION
表获取结果的SELECT
。
table列 表的名字
标题已经通俗易懂了,哈哈
type列 访问类型详解及排列
【type列常见值】
system
:一张表只有一行记录(如系统参数表,每个参数字段只存储一个值),这是const
类型的特例(平时不会出现,这个也可以忽略不计)const
:表示通过索引一次就找到了,const
用于比较primary key
或者unique
索引。因为只匹配一行数据,所以很快。如将主键置于where
列表中,MySQL就能将该查询转化为一个常量。
eq_ref
:唯一性索引扫描,读取本表中和关联表表中的每行组合成的一行,查出来只有一条记录。除了system
和const
类型之外, 这是最好的连接类型。
ref
:非唯一性索引扫描,返回本表和关联表某个值匹配的所有行,查出来有多条记录。
【例子1】
【例子2】
range
:只检索给定范围的行,一般就是在WHERE
语句中出现了BETWEEN
、< >
、in
等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引树的某一点,而结束于另一点,不用扫描全部索引。
index
:Full Index Scan
,全索引扫描,index
和ALL
的区别为index
类型只遍历索引树,不用全表扫描。也就是说虽然ALL
和index
都是读全表,但是index
是从索引中读的,ALL
是从磁盘中读取的。**因为索引文件通常比数据文件小,所以Index往往比ALL快。
ALL
:Full Table Scan
,没有用到索引,全表扫描。
从最好到最差依次是:
system
>const
>eq_ref
>ref
>range
>index
>ALL
。除了ALL
没有用到索引,其他级别都用到索引了,表示全表扫描。
**总结:**多看sql的type,如果是All,争取优化成其他的,一般来说,得保证查询至少达到
range
级别,最好达到ref
。
possible_keys列 哪些索引可能被使用
possible_keys
:显示可能应用在这张表中的索引,一个或者多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key列 哪些索引被实际使用(判断索引是否失效)
key
:实际使用的索引。如果为NULL
,则没有使用索引。
查询中如果使用了覆盖索引,则该索引仅仅出现在key
列表中,不出现在possible_keys中。
key_len列 索引中使用的字节数
key_len
:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。key_len
显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len
是根据表定义计算而得,不是通过表内检索出的。
在不损失精度的情况下,长度越短越好。一般是精度要求更高(根据姓名和城市查询一个人比只根据姓名查询一个人精度更高),key_len越长。
key_len
计算规则:https://blog.csdn.net/qq_34930488/article/details/102931490
ref列 表之间的引用
ref
:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。
由key_len可知t1表的idx col1 col2被充分使用,cl1匹配t2表的col1,co2匹配了一个常量,即’ac’
rows列 每张表有多少行被优化器查询
rows
:根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数
。
【没建索引之前】
【建索引之后】
Extra列 包含不适合在其他列中显示但十分重要的额外信息
Extra列也会有一系列的值,下面是这些值的解释:
Using filesort
:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作成为"文件内排序"。
总结:出现
Using filesort
尽快优化
Using temporary
:使用了临时表(查询结束之后还要删除)保存中间结果,MySQL在対查询结果排序时使用了临时表。常见于排序order by
和分组查询group by
。临时表対系统性能损耗很大。
总结:出现
Using temporary
尽快优化
Using index
:表示相应的SELECT
操作中使用了覆盖索引,避免访问了表的数据行,效率不错
!- 如果同时出现
Using where
,表示索引被用来执行索引键值的查找; - 如果没有同时出现
Using where
,表明索引用来读取数据而非执行查找动作。
覆盖索引:就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说
查询列要被所使用的索引覆盖
。 注意:如果要使用覆盖索引,不要写SELECT *
,要写出具体的字段。
Using where
:表明使用了WHERE
过滤。Using join buffer
:使用了连接缓存。如果join特别多,可以调大配置文件的buffer。impossible where
:WHERE
子句的值总是false,不能用来获取任何元组。
select tables optimized away
:在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算查询执行计划生成的阶段即完成优化。distinct
:找到第一个匹配的元组后停止同样值的动作