韩国情侣网站模板,做网站的原型 免费,wordpress清除无效计划任务,网站做外链怎么样昨天中午在食堂#xff0c;和部门的技术大牛们坐在一桌吃饭#xff0c;作为一个卑微技术渣仔默默的吃着饭#xff0c;听大佬们高谈阔论#xff0c;研究各种高端技术#xff0c;我TM也想说话可实在插不上嘴。聊着聊着突然说到他上午面试了一个工作6年的程序员#xff0c;表…昨天中午在食堂和部门的技术大牛们坐在一桌吃饭作为一个卑微技术渣仔默默的吃着饭听大佬们高谈阔论研究各种高端技术我TM也想说话可实在插不上嘴。聊着聊着突然说到他上午面试了一个工作6年的程序员表情挺复杂他说我看他简历写着熟悉SQL语句调优就问了下 Explain 执行计划怎么看结果这老哥一问三不知工作6年这么基础的东西都不了解感受到了大佬的王之鄙视回到工位我就开始默默写这个哎~ 我TM也不太懂 Explain 老哥你这是针对我啊哭唧唧~Explain有什么用当Explain 与 SQL语句一起使用时MySQL 会显示来自优化器关于SQL执行的信息。也就是说MySQL解释了它将如何处理该语句包括如何连接表以及什么顺序连接表等。表的加载顺序sql 的查询类型可能用到哪些索引哪些索引又被实际使用表与表之间的引用关系一个表中有多少行被优化器查询 .....Explain有哪些信息Explain 执行计划包含字段信息如下分别是 id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、Extra 12个字段。下边我们会结合具体的SQL示例详细的解读每个字段以及每个字段中不同参数的含义以下所有示例数据库版本为 MySQL.5.7.17。mysql select version() from dual;
------------
| version() |
------------
| 5.7.17-log |
------------我们创建三张表 one、two、three表之间的关系 one.two_id two.two_id AND two.three_id three.three_id。Explain执行计划详解一、idid 表示查询中执行select子句或者操作表的顺序id的值越大代表优先级越高越先执行。 id大致会出现 3种情况1、id相同看到三条记录的id都相同可以理解成这三个表为一组具有同样的优先级执行顺序由上而下具体顺序由优化器决定。mysql EXPLAIN SELECT * FROM one o,two t, three r WHERE o.two_id t.two_id AND t.three_id r.three_id;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100 | NULL |
| 1 | SIMPLE | t | NULL | ALL | PRIMARY | NULL | NULL | NULL | 2 | 50 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | r | NULL | eq_ref | PRIMARY | PRIMARY | 4 | xin-slave.t.three_id | 1 | 100 | NULL |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------2、id不同如果我们的 SQL 中存在子查询那么 id的序号会递增id值越大优先级越高越先被执行 。当三个表依次嵌套发现最里层的子查询 id最大最先执行。mysql EXPLAIN select * from one o where o.two_id (select t.two_id from two t where t.three_id (select r.three_id from three r where r.three_name我是第三表2));
-----------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | o | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
| 2 | SUBQUERY | t | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
| 3 | SUBQUERY | r | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
-----------------------------------------------------------------------------------------------------------3、以上两种同时存在将上边的 SQL 稍微修改一下增加一个子查询发现 id的以上两种同时存在。相同id划分为一组这样就有三个组同组的从上往下顺序执行不同组 id值越大优先级越高越先执行。mysql EXPLAIN select * from one o where o.two_id (select t.two_id from two t where t.three_id (select r.three_id from three r where r.three_name我是第三表2)) AND o.one_id in(select one_id from one where o.one_name我是第一表2);
------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
------------------------------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | o | NULL | ALL | PRIMARY | NULL | NULL | NULL | 2 | 50 | Using where |
| 1 | PRIMARY | one | NULL | eq_ref | PRIMARY | PRIMARY | 4 | xin-slave.o.one_id | 1 | 100 | Using index |
| 2 | SUBQUERY | t | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
| 3 | SUBQUERY | r | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
------------------------------------------------------------------------------------------------------------------------------二、select_typeselect_type表示 select 查询的类型主要是用于区分各种复杂的查询例如普通查询、联合查询、子查询等。1、SIMPLESIMPLE表示最简单的 select 查询语句也就是在查询中不包含子查询或者 union交并差集等操作。2、PRIMARYPRIMARY当查询语句中包含任何复杂的子部分最外层查询则被标记为PRIMARY。3、SUBQUERYSUBQUERY当 select 或 where 列表中包含了子查询该子查询被标记为SUBQUERY 。4、DERIVEDDERIVED表示包含在from子句中的子查询的select在我们的 from 列表中包含的子查询会被标记为derived 。5、UNIONUNION如果union后边又出现的select 语句则会被标记为union若 union 包含在 from 子句的子查询中外层 select 将被标记为 derived。6、UNION RESULTUNION RESULT代表从union的临时表中读取数据而table列的union1,4表示用第一个和第四个select的结果进行union操作。mysql EXPLAIN select t.two_name, ( select one.one_id from one) o from (select two_id,two_name from two where two_name ) t union (select r.three_name,r.three_id from three r);---------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | two | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 50 | Using where |
| 2 | SUBQUERY | one | NULL | index | NULL | PRIMARY | 4 | NULL | 2 | 100 | Using index |
| 4 | UNION | r | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100 | NULL |
| NULL | UNION RESULT | union1,4 | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
---------------------------------------------------------------------------------------------------------------------------三、table查询的表名并不一定是真实存在的表有别名显示别名也可能为临时表例如上边的DERIVED、 union1,4等。四、partitions查询时匹配到的分区信息对于非分区表值为NULL当查询的是分区表时partitions显示分区表命中的分区情况。---------------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | p201801,p201802,p201803,p300012 | index | NULL | PRIMARY | 9 | NULL | 3 | 100 | Using index |
---------------------------------------------------------------------------------------------------------------------------------------------五、typetype查询使用了何种类型它在 SQL优化中是一个非常重要的指标以下性能从好到坏依次是system const eq_ref ref ref_or_null index_merge unique_subquery index_subquery range index ALL1、systemsystem 当表仅有一行记录时(系统表)数据量很少往往不需要进行磁盘IO速度非常快。2、constconst表示查询时命中 primary key 主键或者 unique 唯一索引或者被连接的部分是一个常量(const)值。这类扫描效率极高返回数据量少速度非常快。mysql EXPLAIN SELECT * from three where three_id1;
----------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
----------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | three | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100 | NULL |
----------------------------------------------------------------------------------------------------------3、eq_refeq_ref查询时命中主键primary key 或者 unique key索引 type 就是 eq_ref。mysql EXPLAIN select o.one_name from one o ,two t where o.one_id t.two_id ;
-------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | index | PRIMARY | idx_name | 768 | NULL | 2 | 100 | Using index |
| 1 | SIMPLE | t | NULL | eq_ref | PRIMARY | PRIMARY | 4 | xin-slave.o.one_id | 1 | 100 | Using index |
-------------------------------------------------------------------------------------------------------------------------------4、refref区别于eq_ref ref表示使用非唯一性索引会找到很多个符合条件的行。mysql select o.one_id from one o where o.one_name xin ;
--------
| one_id |
--------
| 1 |
| 3 |
--------sql
mysql EXPLAIN select o.one_id from one o where o.one_name xin ;
----------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
----------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | ref | idx_name | idx_name | 768 | const | 1 | 100 | Using index |
----------------------------------------------------------------------------------------------------------------5、ref_or_nullref_or_null这种连接类型类似于 ref区别在于 MySQL会额外搜索包含NULL值的行。mysql EXPLAIN select o.one_id from one o where o.one_name xin OR o.one_name IS NULL;
------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | ref_or_null | idx_name | idx_name | 768 | const | 3 | 100 | Using where; Using index |
------------------------------------------------------------------------------------------------------------------------------------6、index_mergeindex_merge使用了索引合并优化方法查询使用了两个以上的索引。下边示例中同时使用到主键one_id 和 字段one_name的idx_name 索引 。mysql EXPLAIN select * from one o where o.one_id 1 and o.one_name xin;
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | index_merge | PRIMARY,idx_name | idx_name,PRIMARY | 772,4 | NULL | 1 | 100 | Using intersect(idx_name,PRIMARY); Using where |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------7、unique_subqueryunique_subquery替换下面的 IN子查询子查询返回不重复的集合。value IN (SELECT primary_key FROM single_table WHERE some_expr)8、index_subqueryindex_subquery区别于unique_subquery用于非唯一索引可以返回重复值。value IN (SELECT key_column FROM single_table WHERE some_expr)9、rangerange使用索引选择行仅检索给定范围内的行。简单点说就是针对一个有索引的字段给定范围检索数据。在where语句中使用 bettween...and、、、、in 等条件查询 type 都是 range。举个栗子three表中three_id为唯一主键user_id普通字段未建索引。mysql EXPLAIN SELECT * from three where three_id BETWEEN 2 AND 3;
---------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | three | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 1 | 100 | Using where |
---------------------------------------------------------------------------------------------------------------从结果中看到只有对设置了索引的字段做范围检索 type 才是 range。mysql EXPLAIN SELECT * from three where user_id BETWEEN 2 AND 3;
-----------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | three | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
-----------------------------------------------------------------------------------------------------------10、indexindexIndex 与ALL 其实都是读全表区别在于index是遍历索引树读取而ALL是从硬盘中读取。下边示例three_id 为主键不带 where 条件全表查询 type结果为index 。mysql EXPLAIN SELECT three_id from three ;
---------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | three | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 100 | Using index |
---------------------------------------------------------------------------------------------------------------11、ALLALL将遍历全表以找到匹配的行性能最差。mysql EXPLAIN SELECT * from two ;
-----------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | two | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100 | NULL |
-----------------------------------------------------------------------------------------------------六、possible_keyspossible_keys表示在MySQL中通过哪些索引能让我们在表中找到想要的记录一旦查询涉及到的某个字段上存在索引则索引将被列出但这个索引并不定一会是最终查询数据时所被用到的索引。具体请参考上边的例子。七、keykey区别于possible_keyskey是查询中实际使用到的索引若没有使用索引显示为NULL。具体请参考上边的例子。当 type 为 index_merge 时可能会显示多个索引。八、key_lenkey_len表示查询用到的索引长度字节数原则上长度越短越好 。单列索引那么需要将整个索引长度算进去多列索引不是所有列都能用到需要计算查询中实际用到的列。注意key_len只计算where条件中用到的索引长度而排序和分组即便是用到了索引也不会计算到key_len中。九、refref常见的有constfuncnull字段名。当使用常量等值查询显示const当关联查询时会显示相应关联表的关联字段如果查询条件使用了表达式、函数或者条件列发生内部隐式转换可能显示为func其他情况null十、rowsrows以表的统计信息和索引使用情况估算要找到我们所需的记录需要读取的行数。这是评估SQL 性能的一个比较重要的数据mysql需要扫描的行数很直观的显示 SQL 性能的好坏一般情况下 rows 值越小越好。mysql EXPLAIN SELECT * from three;
-----------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | three | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100 | NULL |
-----------------------------------------------------------------------------------------------------十一、filteredfiltered 这个是一个百分比的值表里符合条件的记录数的百分比。简单点说这个字段表示存储引擎返回的数据在经过过滤后剩下满足条件的记录数量的比例。在MySQL.5.7版本以前想要显示filtered需要使用explain extended命令。MySQL.5.7后默认explain直接显示partitions和filtered的信息。十二、ExtraExtra 不适合在其他列中显示的信息Explain 中的很多额外的信息会在 Extra 字段显示。1、Using indexUsing index我们在相应的 select 操作中使用了覆盖索引通俗一点讲就是查询的列被索引覆盖使用到覆盖索引查询速度会非常快SQl优化中理想的状态。什么又是覆盖索引?一条 SQL只需要通过索引就可以返回我们所需要查询的数据一个或几个字段而不必通过二级索引查到主键之后再通过主键查询整行数据select * 。one_id表为主键mysql EXPLAIN SELECT one_id from one ;
------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | index | NULL | idx_two_id | 5 | NULL | 3 | 100 | Using index |
------------------------------------------------------------------------------------------------------------------注意想要使用到覆盖索引我们在 select 时只取出需要的字段不可select *而且该字段建了索引。mysql EXPLAIN SELECT * from one ;
-----------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100 | NULL |
-----------------------------------------------------------------------------------------------------2、Using whereUsing where查询时未找到可用的索引进而通过where条件过滤获取所需数据但要注意的是并不是所有带where语句的查询都会显示Using where。下边示例create_time 并未用到索引type 为 ALL即MySQL通过全表扫描后再按where条件筛选数据。mysql EXPLAIN SELECT one_name from one where create_time 2020-05-18;
-----------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where |
-----------------------------------------------------------------------------------------------------------3、Using temporaryUsing temporary表示查询后结果需要使用临时表来存储一般在排序或者分组查询时用到。mysql EXPLAIN SELECT one_name from one where one_id in (1,2) group by one_name;
-----------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
-----------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | range| NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using temporary; Using filesort |
-----------------------------------------------------------------------------------------------------------4、Using filesortUsing filesort表示无法利用索引完成的排序操作也就是ORDER BY的字段没有索引通常这样的SQL都是需要优化的。mysql EXPLAIN SELECT one_id from one ORDER BY create_time;
--------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
--------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 100 | Using filesort |
--------------------------------------------------------------------------------------------------------------如果ORDER BY字段有索引就会用到覆盖索引相比执行速度快很多。mysql EXPLAIN SELECT one_id from one ORDER BY one_id;
---------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | one | NULL | index | NULL | PRIMARY | 4 | NULL | 3 | 100 | Using index |
---------------------------------------------------------------------------------------------------------------5、Using join bufferUsing join buffer在我们联表查询的时候如果表的连接条件没有用到索引需要有一个连接缓冲区来存储中间结果。先看一下有索引的情况连接条件 one_name 、two_name 都用到索引。mysql EXPLAIN SELECT one_name from one o,two t where o.one_name t.two_name;
---------------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | o | NULL | index | idx_name | idx_name | 768 | NULL | 3 | 100 | Using where; Using index |
| 1 | SIMPLE | t | NULL | ref | idx_name | idx_name | 768 | xin-slave.o.one_name | 1 | 100 | Using index |
---------------------------------------------------------------------------------------------------------------------------------------------接下来删掉 连接条件 one_name 、two_name 的字段索引。发现Extra 列变成 Using join buffertype均为全表扫描这也是SQL优化中需要注意的地方。mysql EXPLAIN SELECT one_name from one o,two t where o.one_name t.two_name;
--------------------------------------------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
--------------------------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 2 | 100 | NULL |
| 1 | SIMPLE | o | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using join buffer (Block Nested Loop) |
--------------------------------------------------------------------------------------------------------------------------------------------------6、Impossible whereImpossible where表示在我们用不太正确的where语句导致没有符合条件的行。mysql EXPLAIN SELECT one_name from one WHERE 12;
----------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
----------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
----------------------------------------------------------------------------------------------------------------7、No tables usedNo tables used我们的查询语句中没有FROM子句或者有 FROM DUAL子句。mysql EXPLAIN select now();
--------------------------------------------------------------------------------------------------------------
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
--------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
--------------------------------------------------------------------------------------------------------------Extra列的信息非常非常多这里就不再一一列举了详见 MySQL官方文档 https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#jointype_index_merge总结上边只是简单介绍了下 Explain 执行计划各个列的含义了解它不仅仅是要应付面试在实际开发中也经常会用到。比如对慢SQL进行分析如果连执行计划结果都不会看那还谈什么SQL优化呢厉害了3万字的MySQL精华总结 面试100问高质量SQL的30条建议(后端必备)