参考文章

  • 第09章_性能分析工具的使用.pdf · 资料文件 · 语雀

    1 Show Status:查看系统性能参数

    在MySQL中,可以使用 SHOW STATUS 语句查询一些MySQL数据库服务器的性能参数 、执行频率 。
    SHOW STATUS语句:SHOW [GLOBAL|SESSION] STATUS LIKE '参数';
    一些常用的性能参数如下:
    image.png

    2 Slow_Query_Log:开启慢查询日志

    详见顶部附件 ```java //1.0 得到返回记录集最多的10个SQL mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log

//2.0 得到访问次数最多的10个SQL mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log

//3.0 得到按照时间排序的前10条里面含有左连接的查询语句 mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/atguigu-slow.log

//4.0 另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况 mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more

  1. <a name="hi4Bv"></a>
  2. # 3 Show Profile:查看SQL整体执行成本
  3. MySQL从5.0.3版本开始增加了对Show Profiles和Show Profile的支持,Show Profiles能够帮我们了解在SQL语句执行过程中时间耗费在了哪些地方。详细使用详见顶部附件。<br />`案例1:mysql > show profile;`<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/696107/1650766607317-279d9f12-d287-4650-9513-e5b74687f1ac.png#clientId=u04412959-674a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=515&id=qXO7B&name=image.png&originHeight=566&originWidth=557&originalType=binary&ratio=1&rotation=0&showTitle=false&size=183389&status=done&style=none&taskId=ue936a33c-7304-4fe7-b868-3b68e3ac548&title=&width=506.36362538850034)<br />`案例2:mysql> show profile cpu,block io for query 2;`<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/696107/1650766529750-b9dd80ae-837f-41ef-862c-271f0c8867c5.png#clientId=u04412959-674a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=395&id=hLzaU&name=image.png&originHeight=515&originWidth=1124&originalType=binary&ratio=1&rotation=0&showTitle=false&size=332181&status=done&style=none&taskId=u78d25917-3d48-4652-96c9-71aeb7c76de&title=&width=862.803955078125)
  4. <a name="zTIoe"></a>
  5. #
  6. <a name="on1NE"></a>
  7. # 4 Explain SQL:MySQL如何被优化执行
  8. <a name="LCI4T"></a>
  9. ## 概述
  10. 官网参考:[Explain-文档](https://dev.mysql.com/doc/refman/5.7/en/explain-output.html)<br />_The _[_EXPLAIN_](https://dev.mysql.com/doc/refman/5.7/en/explain.html)_ statement provides information about how MySQL executes statements. _[_EXPLAIN_](https://dev.mysql.com/doc/refman/5.7/en/explain.html)_ works with _[_SELECT_](https://dev.mysql.com/doc/refman/5.7/en/select.html)_, _[_DELETE_](https://dev.mysql.com/doc/refman/5.7/en/delete.html)_, _[_INSERT_](https://dev.mysql.com/doc/refman/5.7/en/insert.html)_, _[_REPLACE_](https://dev.mysql.com/doc/refman/5.7/en/replace.html)_, and _[_UPDATE_](https://dev.mysql.com/doc/refman/5.7/en/update.html)_ statements._更多是Optimizer查询优化器功能的提现。
  11. - EXPLAIN不考虑各种cache。
  12. - EXPLAIN不能显示MySQL在执行查询时所作的优化工作。
  13. - EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况。
  14. - 部分统计信息是估算的,并非精确值
  15. <a name="DnenQ"></a>
  16. ## 各列的值说明
  17. <a name="b67D0"></a>
  18. ### Id:select查询序列号
  19. 1. id如果不相同,则值越高的优先级越高,越先执行(如子查询)。
  20. 1. id相同时,可以认为是一组,执行顺序由上至下
  21. 总结:值越高先执行,值相等,再顺序执行<br />![](https://cdn.nlark.com/yuque/0/2022/png/696107/1642129955811-f14685d8-5bbb-4e02-8ae4-8b318702a613.png#crop=0&crop=0&crop=1&crop=1&id=zFlzP&originHeight=147&originWidth=778&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
  22. <a name="M5635"></a>
  23. ### select_type:select子句类型
  24. 每个select子句的类型
  25. 1. SIMPLE(简单SELECT,不使用UNION或子查询等)
  26. 1. PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)
  27. 1. UNION(UNION中的第二个或后面的SELECT语句)
  28. 1. DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)
  29. 1. UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)
  30. 1. SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)
  31. 1. DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)
  32. 1. DERIVED(派生表的SELECT, FROM子句的子查询)
  33. 1. UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)
  34. 如下SQL语句
  35. ```sql
  36. EXPLAIN select * from users u1 left join userdata ud1 on u1.userid=ud1.userid
  37. union
  38. select * from users u2 right join (select * from userdata) ud2 on u2.userid=ud2.userid where u2.userid=(select userid from userdata where userid=1);

table:select表名

所访问数据库中表名称

possible_keys:可能利用到的索引

查询可能利用到的索引,一个或多个。如果为null,则可能没有索引将被使用到。需要注意的是:该值代表的是可能涉及到的索引,但不一定被查询实际使用。

★type:select访问类型sc-err-ia

对表访问方式,表示MySQL在表中找到所需行的方式,又称”访问类型”。

  • 结果值从最好到最坏依次是:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。
    • 其中比较重要的几个提取出来(见上图中的蓝色)。
    • SQL性能优化的目标:至少要达到range级别,要求是ref级别,最好是consts级别。(阿里巴巴开发手册要求) | type | 描述
      erri:eq_ref:找到唯一的;ref:找到了几条,range:找到了一个范围,index:索引上全表) | | —- | —- | | system | 是const类型的特例,当表中只有一条记录或者该表使用的存储引擎的统计数据是精确的,比如MyISAM(统计Select count()直接读取一个常量)、Memory,那么对该表的访问方法就是system,一般可忽略不计 | | const | 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,或根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是const。 | | eq_ref | 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配。 在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则对该被驱动表的访问方法就是eq_ref。
      简单来说,就是多表连接中使用primary key或者 unique key作为关联条件。常用于主键和唯一索引扫描。唯一索引的好处就是找到了那条就不用继续找了。 | | ref | 非唯一性扫描,返回匹配某个单独值的所有行。当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是ref。
      本质也是一种索引访问,它返回所有匹配某个单独值的行。然后,它可能会找到多个符合记录的行。是属于查找和扫描的混合体。
      `CREATE index idx_author on news(author);``EXPLAIN select
      from news where author=’秦蓝’;<br />![](https://cdn.nlark.com/yuque/0/2022/png/696107/1642129956527-c5c219d8-9f53-41b0-a921-bb5e5910a45b.png#crop=0&crop=0&crop=1&crop=1&id=SB9xl&originHeight=84&originWidth=564&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)<br /> | | range | 只检索给定范围的行,使用一个索引来选择行。Key列显示使用了哪个索引。一般就是在where语句中出现了between、<>、in等的查询。<br />这种范围扫描索引会比全表扫描(索引)好,因为它只需要开始于索引的某个点,而结束于某个点,不用扫描全部索引。<br />EXPLAIN select * from users u where userid in(1,2,3);`
      11 性能分析工具的观察:4S分析法 - 图2 | | index | Full Index Scan,index与ALL区别为index类型只遍历索引树。
      这通常比ALL快,因为索引文件通常比数据文件小。index从索引中读取(一般索引会常驻于内存),而all是从硬盘中读取。 | | ALL | Full Table Scan, MySQL将遍历全表以找到匹配的行
      11 性能分析工具的观察:4S分析法 - 图3 | | NULL | MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。 |

key:实际利用到的索引

实际使用的索引。如果为null,则没有使用索引。查询中若使用了覆盖索引,则该索引仅出现在key列表中。
show index from users;
EXPLAIN select nickname,mobileno from users;
users表的nickname和mobileno是复合索引,如果使用到了聚集索引,则
11 性能分析工具的观察:4S分析法 - 图4
11 性能分析工具的观察:4S分析法 - 图5

★key_len:实际使用到的索引长度(即字节数)

检查是否充分的利用上了索引,值越大越好(只能跟自己比,不能跟其他变量横向比较),主要针对于联合索引,有一定的参考意义。
一般情况下,值越高,索引的精度越好。在不损失精度的情况下,值越小越好。

key_len是如何计算的? varchar(10)变长字段且允许NULL: 10 ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段) varchar(10)变长字段且不允许NULL: 10 ( character set:utf8=3,gbk=2,latin1=1)+2(变长字段) char(10)固定字段且允许NULL: 10 ( character set:utf8=3,gbk=2,latin1=1)+1(NULL) char(10)固定字段且不允许NULL: 10 ( character set:utf8=3,gbk=2,latin1=1)

ref:驱动表字段/常量

当使用索引列等值查询时,与索引列进行等值匹配的对象信息,就是谁和我做的匹配呢?
案例1:EXPLAIN select * from dream_datas where id=1
image.png
案例2:EXPLAIN select * from users u left join userdata ud on u.userid=ud.userid;
11 性能分析工具的观察:4S分析法 - 图7

★rows:估算出的结果集行数

根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数,是一个估算值

filter:表经过搜索条件过滤后剩余记录条数的百分比

对于单表查询来说,这个filtered列的值没什么意义,我们更关注在连接查询中驱动表的filtered值,它决定了被驱动表要执行的次数(即:rows filtered)。
该属性是MySQL8.0之后新增的属性。
`案例:EXPLAIN select
from dream_datas d inner join userdata ud on d.id=ud.sys_userid where id=1;`
image.png

★Extra:其他额外信息

顾名思义,Extra列是用来说明一些额外信息的,包含不适合在其他列中显示但十分重要的额外信息。我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句。MySQL提供的额外信息有好几十个,我们就不一个一个介绍了,所以我们只挑比较重要的额外信息介绍给大家。
using temporary:急需优化
在许多查询的执行过程中,MySQL可能会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含DISTINCT、GROUP BY、UNION等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。如果查询中使用到了内部的临时表,在执行计划的Extra列将会显示Using temporary提示。建议:急需优化。
EXPLAIN select nickname from users u where u.userid>=999 group by nickname;
11 性能分析工具的观察:4S分析法 - 图9
using filesort:需要优化
很多情况下排序操作无法使用到索引,只能在内存中(记录较少的时候)或者磁盘中(记录较多的时候)进行排序,MySQL把这种在内存中或者磁盘上进行排序的方式统称为文件排序(英文名:filesort)。当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”,说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
简单总结:查询的时候使用到了索引,但是排序的时候没有使用到。建议:需要优化。
EXPLAIN select userid,nickname from users u where u.userid>=999 order by email;
11 性能分析工具的观察:4S分析法 - 图10
join buffer
在做连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度,也就是我们所讲的基于块的嵌套循环算法。
该字段跟索引无关,即使表没有加索引,查询优化器也会根据情况给表连接操作加上join buffer。
using where
当我们使用全表扫描来执行对某个表的查询,并且该语句的WHERE子句中有针对该表的搜索条件时,在Extra列中会提示上述额外信息。
案例1:EXPLAIN select * from dream_datas where id>10
image.png
案例2:EXPLAIN select id from dream_datas where id>10
image.png
using index:覆盖索引
表示相应的select操作中使用了覆盖索引,避免访问了表的数据行(避免了回表),效率不错。如果同时出现了using where,表明索引被用来执行索引键值的查找。如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
EXPLAIN select nickname from users u where u.nickname like '%xx%';
11 性能分析工具的观察:4S分析法 - 图13
EXPLAIN select nickname from users u;
11 性能分析工具的观察:4S分析法 - 图14
using join buffer
该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

using index condition:索引条件下推
详见:14 索引的创建和设计原则

综合案例

  1. #1. table:表名
  2. #查询的每一行记录都对应着一个单表
  3. EXPLAIN SELECT * FROM s1;
  4. #s1:驱动表 s2:被驱动表
  5. EXPLAIN SELECT * FROM s1 INNER JOIN s2;
  6. #2. id:在一个大的查询语句中每个SELECT关键字都对应一个唯一的id
  7. SELECT * FROM s1 WHERE key1 = 'a';
  8. SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';
  9. SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2);
  10. SELECT * FROM s1 UNION SELECT * FROM s2;
  11. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
  12. EXPLAIN SELECT * FROM s1 INNER JOIN s2;
  13. EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
  14. ######查询优化器可能对涉及子查询的查询语句进行重写,转变为多表查询的操作########
  15. EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
  16. #Union去重
  17. EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
  18. EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
  19. #3. select_typeSELECT关键字对应的那个查询的类型,确定小查询在整个大查询中扮演了一个什么角色
  20. # 查询语句中不包含`UNION`或者子查询的查询都算作是`SIMPLE`类型
  21. EXPLAIN SELECT * FROM s1;
  22. #连接查询也算是`SIMPLE`类型
  23. EXPLAIN SELECT * FROM s1 INNER JOIN s2;
  24. #对于包含`UNION`或者`UNION ALL`或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个
  25. #查询的`select_type`值就是`PRIMARY`
  26. #对于包含`UNION`或者`UNION ALL`的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询
  27. #以外,其余的小查询的`select_type`值就是`UNION`
  28. #`MySQL`选择使用临时表来完成`UNION`查询的去重工作,针对该临时表的查询的`select_type`就是
  29. #`UNION RESULT`
  30. EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
  31. EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
  32. #子查询:
  33. #如果包含子查询的查询语句不能够转为对应的`semi-join`的形式,并且该子查询是不相关子查询。
  34. #该子查询的第一个`SELECT`关键字代表的那个查询的`select_type`就是`SUBQUERY`
  35. EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
  36. #如果包含子查询的查询语句不能够转为对应的`semi-join`的形式,并且该子查询是相关子查询,
  37. #则该子查询的第一个`SELECT`关键字代表的那个查询的`select_type`就是`DEPENDENT SUBQUERY`
  38. EXPLAIN SELECT * FROM s1
  39. WHERE key1 IN (SELECT key1 FROM s2 WHERE s1.key2 = s2.key2) OR key3 = 'a';
  40. #注意的是,select_type为`DEPENDENT SUBQUERY`的查询可能会被执行多次。
  41. #在包含`UNION`或者`UNION ALL`的大查询中,如果各个小查询都依赖于外层查询的话,那除了
  42. #最左边的那个小查询之外,其余的小查询的`select_type`的值就是`DEPENDENT UNION`。
  43. EXPLAIN SELECT * FROM s1
  44. WHERE key1 IN (SELECT key1 FROM s2 WHERE key1 = 'a' UNION SELECT key1 FROM s1 WHERE key1 = 'b');
  45. #对于包含`派生表`的查询,该派生表对应的子查询的`select_type`就是`DERIVED`
  46. EXPLAIN SELECT *
  47. FROM (SELECT key1, COUNT(*) AS c FROM s1 GROUP BY key1) AS derived_s1 WHERE c > 1;
  48. #当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,
  49. #该子查询对应的`select_type`属性就是`MATERIALIZED`
  50. EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2); #子查询被转为了物化表
  51. # 4. partition(略):匹配的分区信息
  52. # 5. type:针对单表的访问方法
  53. #当表中`只有一条记录`并且该表使用的存储引擎的统计数据是精确的,比如MyISAMMemory
  54. #那么对该表的访问方法就是`system`。
  55. CREATE TABLE t(i INT) ENGINE=MYISAM;
  56. INSERT INTO t VALUES(1);
  57. EXPLAIN SELECT * FROM t;
  58. #换成InnoDB
  59. CREATE TABLE tt(i INT) ENGINE=INNODB;
  60. INSERT INTO tt VALUES(1);
  61. EXPLAIN SELECT * FROM tt;
  62. #当我们根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是`const`
  63. EXPLAIN SELECT * FROM s1 WHERE id = 10005;
  64. EXPLAIN SELECT * FROM s1 WHERE key2 = 10066;
  65. #在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的
  66. #(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则
  67. #对该被驱动表的访问方法就是`eq_ref`
  68. EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
  69. #当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是`ref`
  70. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
  71. #当对普通二级索引进行等值匹配查询,该索引列的值也可以是`NULL`值时,那么对该表的访问方法
  72. #就可能是`ref_or_null`
  73. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL;
  74. #单表访问方法时在某些场景下可以使用`Intersection`、`Union`、
  75. #`Sort-Union`这三种索引合并的方式来执行查询
  76. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
  77. #`unique_subquery`是针对在一些包含`IN`子查询的查询语句中,如果查询优化器决定将`IN`子查询
  78. #转换为`EXISTS`子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的`type`
  79. #列的值就是`unique_subquery`
  80. EXPLAIN SELECT * FROM s1
  81. WHERE key2 IN (SELECT id FROM s2 WHERE s1.key1 = s2.key1) OR key3 = 'a';
  82. #如果使用索引获取某些`范围区间`的记录,那么就可能使用到`range`访问方法
  83. EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c');
  84. #同上
  85. EXPLAIN SELECT * FROM s1 WHERE key1 > 'a' AND key1 < 'b';
  86. #当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是`index`
  87. EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';
  88. #最熟悉的全表扫描
  89. EXPLAIN SELECT * FROM s1;
  90. #6. possible_keyskey:可能用到的索引 实际上使用的索引
  91. EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';
  92. #7. key_len:实际使用到的索引长度(即:字节数)
  93. # 帮你检查`是否充分的利用上了索引`,`值越大越好`,主要针对于联合索引,有一定的参考意义。
  94. EXPLAIN SELECT * FROM s1 WHERE id = 10005;
  95. EXPLAIN SELECT * FROM s1 WHERE key2 = 10126;
  96. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
  97. EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a';
  98. EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b';
  99. EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b' AND key_part3 = 'c';
  100. EXPLAIN SELECT * FROM s1 WHERE key_part3 = 'a';
  101. #练习:
  102. #varchar(10)变长字段且允许NULL = 10 * ( character setutf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
  103. #varchar(10)变长字段且不允许NULL = 10 * ( character setutf8=3,gbk=2,latin1=1)+2(变长字段)
  104. #char(10)固定字段且允许NULL = 10 * ( character setutf8=3,gbk=2,latin1=1)+1(NULL)
  105. #char(10)固定字段且不允许NULL = 10 * ( character setutf8=3,gbk=2,latin1=1)
  106. # 8. ref:当使用索引列等值查询时,与索引列进行等值匹配的对象信息。
  107. #比如只是一个常数或者是某个列。
  108. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
  109. EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
  110. EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s2.key1 = UPPER(s1.key1);
  111. # 9. rows:预估的需要读取的记录条数
  112. # `值越小越好`
  113. EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';
  114. # 10. filtered: 某个表经过搜索条件过滤后剩余记录条数的百分比
  115. #如果使用的是索引执行的单表扫描,那么计算时需要估计出满足除使用
  116. #到对应索引的搜索条件外的其他搜索条件的记录有多少条。
  117. EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'a';
  118. #对于单表查询来说,这个filtered列的值没什么意义,我们`更关注在连接查询
  119. #中驱动表对应的执行计划记录的filtered值`,它决定了被驱动表要执行的次数(即:rows * filtered)
  120. EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';
  121. #11. Extra:一些额外的信息
  122. #更准确的理解MySQL到底将如何执行给定的查询语句
  123. #当查询语句的没有`FROM`子句时将会提示该额外信息
  124. EXPLAIN SELECT 1;
  125. #查询语句的`WHERE`子句永远为`FALSE`时将会提示该额外信息
  126. EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
  127. #当我们使用全表扫描来执行对某个表的查询,并且该语句的`WHERE`
  128. #子句中有针对该表的搜索条件时,在`Extra`列中会提示上述额外信息。
  129. EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
  130. #当使用索引访问来执行对某个表的查询,并且该语句的`WHERE`子句中
  131. #有除了该索引包含的列之外的其他搜索条件时,在`Extra`列中也会提示上述额外信息。
  132. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' AND common_field = 'a';
  133. #当查询列表处有`MIN`或者`MAX`聚合函数,但是并没有符合`WHERE`子句中
  134. #的搜索条件的记录时,将会提示该额外信息
  135. EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'abcdefg';
  136. EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'NlPros'; #NlPros s1表中key1字段真实存在的数据
  137. #select * from s1 limit 10;
  138. #当我们的查询列表以及搜索条件中只包含属于某个索引的列,也就是在可以
  139. #使用覆盖索引的情况下,在`Extra`列将会提示该额外信息。比方说下边这个查询中只
  140. #需要用到`idx_key1`而不需要回表操作:
  141. EXPLAIN SELECT key1,id FROM s1 WHERE key1 = 'a';
  142. #有些搜索条件中虽然出现了索引列,但却不能使用到索引
  143. #看课件理解索引条件下推
  144. EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
  145. #在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为
  146. #其分配一块名叫`join buffer`的内存块来加快查询速度,也就是我们所讲的`基于块的嵌套循环算法`
  147. #见课件说明
  148. EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
  149. #当我们使用左(外)连接时,如果`WHERE`子句中包含要求被驱动表的某个列等于`NULL`值的搜索条件,
  150. #而且那个列又是不允许存储`NULL`值的,那么在该表的执行计划的Extra列就会提示`Not exists`额外信息
  151. EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;
  152. #如果执行计划的`Extra`列出现了`Using intersect(...)`提示,说明准备使用`Intersect`索引
  153. #合并的方式执行查询,括号中的`...`表示需要进行索引合并的索引名称;
  154. #如果出现了`Using union(...)`提示,说明准备使用`Union`索引合并的方式执行查询;
  155. #出现了`Using sort_union(...)`提示,说明准备使用`Sort-Union`索引合并的方式执行查询。
  156. EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
  157. #当我们的`LIMIT`子句的参数为`0`时,表示压根儿不打算从表中读出任何记录,将会提示该额外信息
  158. EXPLAIN SELECT * FROM s1 LIMIT 0;
  159. #有一些情况下对结果集中的记录进行排序是可以使用到索引的。
  160. #比如:
  161. EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10;
  162. #很多情况下排序操作无法使用到索引,只能在内存中(记录较少的时候)或者磁盘中(记录较多的时候)
  163. #进行排序,MySQL把这种在内存中或者磁盘上进行排序的方式统称为文件排序(英文名:`filesort`)。
  164. #如果某个查询需要使用文件排序的方式执行查询,就会在执行计划的`Extra`列中显示`Using filesort`提示
  165. EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;
  166. #在许多查询的执行过程中,MySQL可能会借助临时表来完成一些功能,比如去重、排序之类的,比如我们
  167. #在执行许多包含`DISTINCT`、`GROUP BY`、`UNION`等子句的查询过程中,如果不能有效利用索引来完成
  168. #查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。如果查询中使用到了内部的临时表,在执行
  169. #计划的`Extra`列将会显示`Using temporary`提示
  170. EXPLAIN SELECT DISTINCT common_field FROM s1;
  171. #EXPLAIN SELECT DISTINCT key1 FROM s1;
  172. #同上。
  173. EXPLAIN SELECT common_field, COUNT(*) AS amount FROM s1 GROUP BY common_field;
  174. #执行计划中出现`Using temporary`并不是一个好的征兆,因为建立与维护临时表要付出很大成本的,所以
  175. #我们`最好能使用索引来替代掉使用临时表`。比如:扫描指定的索引idx_key1即可
  176. EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;
  177. #json格式的explain
  178. EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2
  179. WHERE s1.common_field = 'a';

升级功能

Expalin的四种输出格式

  • Json格式可以看到执行成本的信息:EXPLAIN FORMAT=JSON select * from dream_datas d inner join dream_datas d2 on d.id=d2.ordering;

    SHOW WARNINGS的使用

    在我们使用EXPLAIN语句查看了某个查询的执行计划后,紧接着还可以使用SHOW WARNINGS语句查看与这个查询的执行计划有关的一些扩展信息
    image.png

    6 其他调试分析命令和工具

    Last_Query_Cost

    统计简单SQL查询的执行成本

    Trace

    分析优化器执行计划

    Sys Schema

    监控分析视图

    如查看未使用的索引 image.png

详见:第09章_性能分析工具的使用.pdf · 资料文件 · 语雀