一本彻底搞懂MySQL索引优化EXPLAIN百科全书

Posted Java爱好者社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一本彻底搞懂MySQL索引优化EXPLAIN百科全书相关的知识,希望对你有一定的参考价值。


(
datetime PRIMARY ( (( ((
AUTO_INCREMENT,
PRIMARY ( (((user_group;
(
PRIMARY ( ( ( (user_group ( (... outer_tables
expr (... inner_tables ...) ...

* (c derived_u c>* (user_id user_group = = (
AUTO_INCREMENT,
PRIMARY ( idx_s_name (s_name), idx_s_no (s_no), (BTREE AUTO_INCREMENT=staff(staff(staff(staff(staff(staff(staff idx_remark (remark);

staff idx_remark_part, idx_remark_part(remark(5));

再次执行查询:

EXPLAIN SELECT * FROM staff where remark LIKE \'xxx%\';


索引优化总结


上面列了大部分场景索引最佳实战,除此之外,不宜建索引的几点小总结:

1)更新非常频繁字段不宜建索引

因为字段更新台频繁,会导致B+树的频繁的变更,重建索引。所以这个过程是十分消耗数据库性能的。

2)区分度不大的字段不宜建索引

比如类似性别这类的字段,区分度不大,建立索引的意义不大。因为不能有效过滤数据,性能和全表扫描相当。另外注意一点,返回数据的比例在 30% 之外的,优化器不会选择使用索引。

3)业务中有唯一特性的字段,建议建成唯一索引

业务中如果有唯一特性的字段,即使是多个字段的组合,也尽量都建成唯一索引。尽管唯一索引会影响插入效率,但是对于查询的速度提升是非常明显的。此外,还能够提供校验机制,如果没有唯一索引,高并发场景下,可能还会产生脏数据。

4)多表关联时,要确保关联字段上必须有索引

5)创建索引时避免建立错误的认识

索引越多越好,认为一个查询就需要建一个索引。

宁缺勿滥,认为索引会消耗空间、严重拖慢更新和新增速度。

抵制唯一索引,认为业务的唯一性一律需要在应用层通过“先查后插”方式解决。

过早优化,在不了解系统的情况下就开始优化。

6)最佳索引实践口诀

如果你觉得上面哪些太啰嗦,有朋友已总结为一套优化口诀,优化SQL时也能提个醒吧。

全值匹配我最爱,最左前缀要遵守;

带头大哥不能死,中间兄弟不能断;

索引列上少计算,范围之后全失效;

Like百分写最右,覆盖索引不写星;

不等空值还有or,索引失效要少用;

VAR引号不可丢,SQL高级也不难!

7)EXPLAIN 执行计划实践总结

如果还是觉得 EXPLAIN 执行计划列太多了,也记不住呀,那么请重点关注以下几列:

第1列:ID越大,执行的优先级越高;ID相等,从上往下优先顺序执行。

第2列:select_type 查询语句的类型,SIMPLE简单查询,PRIMARY复杂查询,DERIVED衍生查询(from子查询的临时表),派生表。

第4列:请重点掌握,type类型,查询效率优先级:system->const->eq_ref->ref->range->index->ALL

ALL 是最差的,system 是最好的,性能最佳,阿里巴巴开发规约中要求最差也得到 range 级别,而不能有 index、ALL

最后,对于后端工程师而言,尽力都能掌握 EXPLAIN 的使用,写完SQL请习惯性的用它帮助你分析一下,做一个对SQL性能有追求的程序员,因为SQL也是程序员必备技能,将慢查询问题拍死在项目上线前夕。

如果觉得本文有所收获,欢迎转发分享。

参考资料: 

mysql官网 

https://www.cnblogs.com/songwenjie/p/9402295.html 

https://www.cnblogs.com/phpdragon/p/8231533.html



END



往期精选

看完这篇文章,线上再遇到Jedis「Redis客户端」异常相信你不再怕了!


 你还在为 HTTP 的这些概念头疼吗?


 用『逐步排除法』定位Java服务线上系统性故障


由一次线上故障来理解下TCP三握、四挥 & Java堆栈分析到源码的探秘

扫描下方二维码关注,原创干货及时推送

专注于分享Java后端相关技术、老司机实战干货,不限于JVM、并发、设计模式、性能优化、分布式&微服务、云原生相关主题。


嗨,动动你那Coding的小手点个在看吧


MySQL索引优化-性能分析Explain(转)

1. explain是什么

使用explain关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。

2. explain能干吗

可用于分析:

  • 表的读取顺序
  • 数据读取操作的操作类型
  • 哪些索引可以使用
  • 哪些索引被实际使用
  • 表之间的引用
  • 每张表有多少行被优化器查询

3. explain怎么分析

执行计划包含的信息如下:

技术图片

各字段解释

3.1 id

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序。

分为三种情况:

3.1.1 id相同,执行顺序由上至下

技术图片

id相同,执行顺序从上到下,搭配table列进行观察可知,执行顺序为t1->t3->t2。

3.1.2 id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

技术图片

如果是子查询id的序号会递增,id值越大执行优先级越高,搭配table列可知,执行顺序为t3->t1->t2。

3.1.3 id相同不同,同时存在

技术图片

id如果相同,可认为是同一组,执行顺序从上到下。在所有组中,id值越大执行优先级越高。所以执行顺序为t3->derived2(衍生表,也可以说临时表)->t2。

总结:id的值表示select子句或表的执行顺序,id相同,执行顺序从上到下,id不同,值越大的执行优先级越高。

3.2 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

3.3 table

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

3.4 type

访问类型,显示查询使用了何种类型,从最好到最差依次是:system > const > eq_ref > ref > range > index > ALL。一般来说,要保证查询至少达到range级别,最好能达到ref。

  • system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现
  • const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,索引很快,如将主键置于where列表中,MySQL就能将该查询转换为一个“常量”

技术图片

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

技术图片

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

技术图片

eq_ref和ref:就好比一个班级里面,只有一个班主任和一群学生,t2返回的只有一个记录(就就好比班主任),而col1返回的是所有col1等于ac(所有名字是ac的学生)

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

技术图片

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

技术图片

  • ALL:Full Table Scan(全表扫描),将遍历全表以找到匹配的行

技术图片

3.5 possible_keys

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

简而简之:possible_keys是MySQL分析出推测可能用到的索引有哪几个,而key最后实际用到的索引。(理论:聚会中大概能来多少人和实际来多少人的区别)

3.6 key

  • 实际使用的索引,如果为NULL,则没有使用索引。(要么没建索引,要么建了索引没用,所谓索引失效)
  • 查询中若使用了覆盖索引,则该索引仅出现在key列表中

覆盖索引演示:

技术图片

  • possible_keys,key演示:

技术图片

举例:理论上t1这张表应该用到PRIMARY,idx_t1这两个索引,但是实际中却只用到了idx_t1这一个月索引。

3.7 key_len

  • 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好。(投入少,产物丰富,话句话说就是:既不给马儿吃草,又让马儿跑)
  • key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索获得的(同样的查询结果,key_len用的越少越好)

举例:假设班级的表中在名字列加上一个索引,我们要根据名字去查询名字姓李的,此时我们还想去根据城市去查询,此时的查询条件比单查名字时更精确。此时得到的结果更精确,但是却投入了更多的条件(第一次名字,第二个城市),此时的key_len会比上一次更多。

下图所示:

技术图片

3.8 ref

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

技术图片

MySQL处理这条语句顺序加载(t2)后加载t1,t1中ref为shared.t2.col1和const,前面代表shared库中t2表的col1字段在和t2表中的col1做关联,const代表t1表中的col2匹配了ac这个常量。

3.9 rows

根据表统计信息及索引选用情况,大致估算出找到所需记录所需要读取的行数(被张表有多行被优化器优化过)

技术图片

没有索引的时候,两张表关联后的加载顺序t2>t1,type就是上面介绍eq_ref和ref区别是介绍的班主任(唯一扫描,只有一行匹配),t2.col1=‘ac‘就好比这个班级是ac,两个表加起来读取了641行。

技术图片

建立复合索引之后(idx_col1_col2),所需要读取的行数为143。

3.10 Extra

包含不适合在其他列中显示但十分重要的额外信息

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

技术图片

Extra中包含Using where和Using index,确实看到了where条件和使用了索引(idx_col1_col2_col3),还出现了Using filesort,也就说索引只是部分使用到了。我们知道索引干两件事(排序、快速查询),查询的时候部分使用到了(key不为null并且有值,而且type是ref,ref为const),但是排序没有用到。

技术图片

前一章索引有点说过(查询中排序的字段,排序的字段若通过索引去访问将大大提高排序速度),MySQL自我分析之后的结果和前者的区别:从两条SQL中可以看出修改后后者SQL性能高于前者,后者你怎么修的路(索引),那么我就按照你修好的路走(索引),但是前者也同样查出来了,但是修的路,并没有都走,而是自己在内部产生了一次排序,同比性能,前者在内部自己折腾了一次进行了排序结果得到的结果并没有后者好。(建立索引之后,尽可能访问的时候也是按照索引的顺序)

  • Using temporary: 同比前者性能更差,使用了临时表保存中间结果,MySQL在对查询结果排序时使用了临时表。常见于排序order by和分组查询group by。

tip:临时表的创建是很伤系统性能的,因为搬数据搬到临时表,用完之后再把临时表回收,数据库内部要自己折腾,这时候查询数据几百万几千万条数据,空间要申请的多,搬数据也多,最后还需要释放,严重增加数据库负担。

技术图片

 

技术图片

分析两者SQL,使用复合索引(idx_col1_col2,只要key不为null就是用到索引不要怀疑),这种SQL就慢的无比,如果数据是百万千万级别系统会被拖慢。避免临时表的创建。

  • Using index: 表示相应的SELECT操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率还可以

1. 如果同时出现Using where,表明索引被用来执行索引键值的查找;

技术图片

创建的索引中包含col1和col2,并且查询的列就包含col2,部分列跟索引重合匹配。

2. 如果没有出现同时出现Using where,表明索引用来读取数据而非执行查找动作

技术图片

同上,只是SQL中并没有根据条件去查找

覆盖索引(Covering Index),一说为索引覆盖

理解方式一:就是select的数据列只用从索引中就能够获取,不比读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,换句话说查询列要被所见的索引覆盖。(也就是说建的索引是col1,col2,col3的复合索引,刚好查询的也是这几列或者部分满足)

理解方式二:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的行,因此他不比读取整个行,毕竟索引叶子节点存储了他们所引用的数据,当能通过读取索引就可以得到想要的数据,那就不需要读取行了,一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。

注意:如果使用覆盖索引,一定要注意select列表中只去除需要的列,不可select *;因为如果将所有的字段一起做索引会导致索引文件过大,查询性能下降。

  • Using where: 使用了where过滤
  • Using join buffer: 使用了连接缓存
  • 索引优化MIN/MAx操作或者对于MyIsam存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成阶段即完成优化。Impossible WHERE: where子句值总是false,不能用来获取任何数据,如name=‘张三’ and name=‘李四’(不可能一个人名字是张三,又是李四吧)
  • SELECT tables optimized away: 在没有group by子句的情况下,基于
  • distinct: 优化distinct操作,在找到第一匹配的元组后即停止找同样值的动作

 

热身Case

技术图片

第一行(执行顺序4):id列为1,表示union里的第一个select,select_type列的primary表示查询为外层查询,table列被标记为<devied3>,表示查询结果来自一个衍生表,其中derived3中3代表该查询衍生自第三个select查询,即id为3的select。【select d1,name...】

第二行(执行顺序2):id为3,是整个查询中第三个select的一部分,因查询包含在from中,所以为derived。【select id,name from t1 where other_column=‘‘】

第三行(执行顺序3):select列表中的子查询select_type为subquery,为整个查询中的第二个select。【select id from t3】

第四行(执行顺序1):select_type为union,说明第四个select是union里的第二个select,直接执行【select name,id from t2】

第五行(执行顺序5):代表从union的临时表中读取行的阶段,table列的<union1,4>表示用第一行和第四行的select结果进行union操作。【两个结果union操作】

 

转自:

https://zhuanlan.zhihu.com/p/56529089

 

以上是关于一本彻底搞懂MySQL索引优化EXPLAIN百科全书的主要内容,如果未能解决你的问题,请参考以下文章

彻底搞懂这些 MySQL 技术难点,我只用了10分钟!

mysql优化----explain的列分析

还不懂MySQL索引?这1次彻底搞懂B+树和B-树

MySQL索引优化-性能分析Explain(转)

MySQL优化之索引篇: Explain工具

MySQL索引及Explain及常见优化