MySql Query Optimizer

  1. Mysql 中有专门负责优化 select 语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为了客户端请求的 Query 提供他认为最优的执行计划(它认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分是最耗时间的)
  2. 当客户端向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 , iostal 和 vmstat 来查看系统的性能状态

Explain

是什么(执行计划)

使用Explain 关键字可以模拟优化器执行 sql 查询语句,从而知道 Mysql 是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。
Mysql官网 Explain out format 官方网站

能干嘛

  • 表的读取顺序(id)
    • Explain中的id字段展示出了“表的读取顺序”
  • 数据读取操作的操作类型(select_type)
    • 对应Explain中的select_type字段
  • 那些索引可以使用(possible_keys)
    • 对应Explain中的possible_keys字段
  • 那些索引被实际使用(key)
    • 对应Explain中的key字段
  • 表之间的引用(ref)
    • 对应Explain中的ref字段
  • 每张表有多少行被物理查询(rows)

    • 对应Explain中的rows字段

      怎么玩

  • Explain + sql语句

  • 执行计划包含的信息 | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- |

image.png

各字段解释

id

  • select 查询的序列号,包含一组数字,表示查询中执行 select 子句或操作表的顺序
  • 又分为三种顺序
    • id 相同,执行顺序从上到下

image.png

  1. explain select * from tbl_emp e inner join tbl_dept d on e.deptld = d.id;
  2. --id相同,执行顺序由上向下
  • id不同,如果是子查询,id序号会递增,id值越大优先级越高,越先被执行

image.png

  1. explain select * from tbl_emp
  2. where deptld =(select id from tbl_dept where deptname = 'MK');
  • id相同又不同,同时存在 先从大到小,然后再从上到下

image.png
id如果相同,可以认为是一组,从上往下执行,在所有组中id值越大,优先级越高,越先执行。

  • id 号每个号码,表示一趟独立的查询。一个sql的查询趟数越少越好


select_type

  • 有哪些类型(Mysql 8.0官网介绍)
  • image.png | select_type Value | 意思 | | —- | —- | | SIMPLE | 简单select(不使用UNION或子查询) | | PRIMARY | 最外层的select(例如 鸡蛋壳) | | UNION | 若在第二个select 出现在 union 之后则被标记为union,若union包含在from子句查询中,外层的select将被标记为derived | | DEPENDENT UNION | UNION中的第二个或更高的SELECT语句,取决于外部查询 | | UNION RESULT | 从UNION 表获取结果的 select | | SUBQUERY | 子查询中的第一个 select | | DEPENDENT SUBQUERY | 子查询而第一个select 依赖于外部查询 | | DERIVED | 在from列表中包含子查询被标记为 derived(衍生) Mysql 会递归执行这些子查询,把结果放在临时表中 | | MATERIALIZED | 实现子查询 | | UNCACHEABLE SUBQUERY | 不能缓存其结果的子查询,并且必须针对外部查询的每一行重新求值 | | UNCACHEABLE UNION | union中属于不可缓存子查询的第二个或更高版本的select |

image.png

table

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

partitions

查询将从中匹配记录的分区。对于未分区的表,该值为NULL。

type

  • 连接类型
  • 显示查询使用了何种类型 从最佳到最差依次是: system > const > eq_ref > ref > range > index > ALL。
  • system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 一般来说 ,得保证查询至少达到 range 级别,最好能达到 ref 。
  • 如果你的type类型为 all 并且你的数据在百万以上的请你优化你的sql语句
  • system
    • 表只有一行数据(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
  • const
    • 表示通过索引一次就找到了,const 用于比较 primary key 或者 union 索引。因为只匹配一行数据,所以很快如将逐渐置于 where 列表中, mysql 就能将该查询转换为 一个常量

image.png
这类扫描效率极高,返回的数据少,效率块。

  • eq_ref
    • 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

image.png

eq_ref扫描的条件为,对于前表的每一行(row),后表只有一行被扫描。 再细化一点: (1)join查询; (2)命中主键(primary key)或者非空唯一(unique not null)索引; (3)等值连接; 如上例,id是主键,该join查询为eq_ref扫描。 这类扫描的速度也异常之快。

  • ref
    • 非唯一索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而它可能找会找打多个符合条件的行,所以它应该属于查找和扫描的混合体

image.png

当id改为普通非唯一索引后,常量的连接查询,也由const降级为了ref,因为也可能有多于一行的数据被扫描。 ref扫描,可能出现在join里,也可能出现在单表普通索引里,每一次匹配可能有多行数据返回,虽然它比eq_ref要慢,但它仍然是一个很快的join类型。

  • range
    • 只检索给定范围的行,使用一个索引来选择行,key列显示使用了那个索引,一般就是在你 where 语句中出现 between < , > , in等语句 ,这种扫描索引比全表扫描要好,因为它只需要开始与索引的某一点,而结束语另一点,不用全表扫描

image.png

  • all
    • full table scan 将遍历全表以找到匹配的行

image.png

  • index
    • Full Index Scan,index与ALL区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)

image.png

possiblekeys

  • 显示可能应用在这张表中的索引,一个或多个。查询设计到的字段上若存在索引,则改索引将被列出,但不一定被查询实际使用

    key

  • 实际使用的索引,如果为null ,则没有使用索引

image.png

  • 查询中使用了覆盖索引,则改索引仅出现在 key 列表中

    key_len

  • 表示索引中使用的字节数,可通过该列计算查询中使用的索引长度。在不损失精确性的情况下长度越短越好,

  • key_len 显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出来的(key_len 越大越好)

image.png

同样查询结果的前提下,精度越小越好

ref

  • 显示索引的哪一列被使用了,如果可能的话,是一个常数,那些列或常量被用于查找索引列上的值

image.png

查询中与其它关联表的字段,外键关联建立索引

rows

  • rows 列显示Mysql 认为它执行查询时必须检查的行数 (值越小越好)

    filtered

    filtered列表示将按表条件筛选的表行的估计百分比。最大值为100,这意味着没有对行进行筛选。值从100减小表示过滤量增加。rows显示检查的估计行数,rows×filtered显示将与下表联接的行数。例如,如果rows为1000,filtered为50.00(50%),则要与下表联接的行数为1000×50%=500。

Extra

  • 包含不适合在其它列中显示但十分重要的额外信息
  • Using filesort(九死一生)

    说明Mysql 会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。Mysql 中无法利用索引完成的排序操作称为 文件排序

如何出现问题?
image.png
如何解决这个问题呢?
image.png

  • Using temporary(十死无生)

    使用了临时表保存中间结果,Mysql 在对查询结果排序时使用临时表。常见于group by 和 order by

如何出现这个问题?
image.png
如何解决这个问题呢?( group by 包含一个排序 和 分组)
image.png

  • Using index

    表示相应的 select 操作中使用了覆盖索引( Covering Index) ,避免了访问了表的数据行,效率不错! 如果同时出现 Using where ,表明索引被用来执行索引键值的查找 如果没有同时出现 Using where 表明索引用来读取数据而非执行查找工作。

如果同时出现?
image.png
只有一个?
image.png
覆盖索引(Covering Index)

覆盖索引( Covering Index) 一般为 索引覆盖 理解方式1. select 的数据列只用从索引中就能获取得,不必读取数据行,Mysql 可以利用索引返回select 列表中的字段,而不必根据索引再次读取数据文件,换句话说就是 查询列要被所创建的索引覆盖。 理解方式2. 索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此不必读取整个行,毕竟索引叶子节点存储了它们索引的数据,当能通过读取索引就可以得到想要的数据,那就不需要读取行了,一个索引包含了(或覆盖了)满足查询结果的数据叫做覆盖索引。 注意: 1.如果使用了覆盖索引,一定要注意 select 列表中只取出需要的列,不可 select *。 2.因为如果将所有字段一起做索引会导致文件过大,查询性能下降。

  • Using where
    • 表名使用了 where 过滤
  • Using join buffer
    • 使用了连接缓存
  • Impossible WHERE

    WHERE子句总是false,不能用来获取任何元组

如何出现这个问题?
image.png

  • Select tables optimized away

    在没有 group by子句的情况下,基于索引优化 min/max 操作或者对于 Myisam 存储引擎优化 count(*) 操作,不必等到执行阶段在进行计算,查询执行计划生成的阶段即完成优化

  • Distinct

    优化 distinct 操作,第一个匹配的行后,它将停止为当前行组合搜索更多行

case

自己写出下列的执行排序?
image.png
image.png