本文共 8238 字,大约阅读时间需要 27 分钟。
本文基于MySQL 5.7编写,对于其它版本也适用
(一)执行计划概述
什么是执行计划呢?SQL是一种傻瓜式语言,每一个条件就是一个需求,访问的顺序不同就形成了不同的执行计划。MySQL必须做出选择,一次只能有一种访问路径,一个访问路径就是一个执行计划。通常一条SQL有多个执行计划,那我们如何选择?MySQL数据库与Oracle一样,使用的是基于开销(cost)的优化器策略,那种执行开销更低,就意味着性能更好,速度更快,MySQL就选择哪一种。
(二)执行计划的查看
MySQL数据库的执行计划可以通过explain关键字查看,使用explain可以查看SELECT,DELETE,INSERT,REPLACE,UPDATE语句的执行计划。对于SELECT语句,还可以使用SHOW WARNINGS查看额外的执行计划信息。可以在数据库中查看explain的帮助:mysql> help explain Name: 'EXPLAIN' Description: Syntax: {EXPLAIN | DESCRIBE | DESC} tbl_name [col_name | wild]{EXPLAIN | DESCRIBE | DESC} [explain_type] {explainable_stmt | FOR CONNECTION connection_id}explain_type: { EXTENDED | PARTITIONS | FORMAT = format_name } format_name: { TRADITIONAL | JSON }explainable_stmt: { SELECT statement | DELETE statement | INSERT statement | REPLACE statement | UPDATE statement }
需要注意的是:
1.在早期的MySQL版本中,使用EXTENDED查看扩展信息,目前默认已经启用了扩展信息的输出,因此该参数显得多余了,在MySQL 8.0中已经移除该参数。2.在早期版本中,分区信息是使用EXPLAIN PARTITIONS输出的,目前已经默认开启了分区信息的输出,该参数也已经不再需要,在MySQL 8.0中已经移除该参数。3.不能在同一个EXPLAIN中同时使用EXTENDED和PARTITIONS关键字,这2个关键字都不能与FORMAT关键字一起使用。FORMAT参数用于选择输出格式,一共有2种输出格式:
-- TRADITIONAL :以表格显示输出,默认模式-- JSON :以json格式输出此外,在MySQL 8.0中还提供了TREE方式输出,这里暂时不作了解,后面单独说明。总结一下,EXPLAIN的语法看着较为复杂,实则非常简单。在去除过时参数后,真正可选的参数只有一个FORMAT=json,其它参数都不用选。所以最终执行计划有2种输出形式:
-- 以表格格式输出执行计划,默认方式EXPLAIN sql_stmt-- 以json格式输出执行计划EXPLAIN FORMAT=JSON sql_stmt
mysql> explain select empno,ename,job from dept a join emp b where a.deptno = b.deptno; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+ | 1 | SIMPLE | a | NULL | index | PRIMARY | PRIMARY | 4 | NULL | 4 | 100.00 | Using index | | 1 | SIMPLE | b | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 10.00 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------------------------------------------------+2 rows in set, 1 warning (0.00 sec)
再以JSON格式查看执行计划:
mysql> explain format = json select empno,ename,job from dept a join emp b where a.deptno = b.deptno;
… 结果略 …
可以看到,两种格式输出的信息基本相同,但是也存在不一样的地方,个人觉得最大的区别在于:josn格式的执行计划把cost给展示出来了,MySQL优化器是基于cost选择执行计划的,查看cost对于调优很重要。但是,在实际的使用过程中,我们往往会以表格的形式查看执行计划,因为表格形式的执行计划较为简练,便于我们查看。本文在讲解执行计划时,也只使用表格格式。
这里解释各个列的含义:
例子2:id不同,执行顺序为id从大到小,下面例子的顺序为:t3 --> t2 –> t1
例子3:id相同又不同。id相同,可以认为一组,从上往下执行,在所有组中,id值越大,优先级越高,越先执行。下面例子的执行顺序为:t1 --> <derived2> --> t2
create table t_part_table( id int primary key, col2 varchar(20)) PARTITION by range(id) ( partition p100 values less than(100), partition p200 values less than(200), partition p300 values less than(300), partition p400 values less than(400), partition p500 values less than(500), partition p600 values less than(600), partition p700 values less than(700), partition p800 values less than(800), partition p900 values less than(900), partition p_max values less than MAXVALUE ); CREATE DEFINER=`root`@`%` PROCEDURE `p_insert_part_table`() BEGIN #Routine body goes here... DECLARE str1 varchar(30); DECLARE i int;set i = 1;while i <= 2000 do set str1 = substring(md5(rand()),1,15); insert into t_part_table(id,col2) values(i,str1); set i = i + 1; end while;END
/
使用分区的样例如下:
例子2:这里在查询t2、t3表的时候,使用主键进行查询,并且使用t1.id列与主键进行比较过滤,选择合适的列
(3.2)执行计划连接类型typeexplain的type列表示表的连接类型,从最佳到最差类型如下(其中黑体部分是常见的重点类型):System --> const --> eq_ref --> ref --> fulltext --> ref_or_unll --> index_merge --> unique_subquery --> index_subquery --> range --> index --> ALL
这里解释各个类型的含义:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
value IN (SELECT key_column FROM single_table WHERE some_expr)
(3.3)执行计划额外信息Extra
Extra列用于输出EXPLAIN的额外信息,这里说明了可以在此列中显示的值,因为这只值较多,这里先列出重要的信息和我能理解的信息,有的参数翻译过来实在不理解什么意思,就忽略了,见谅。
SELECT * FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.id IS NULL
假设t2.id定义为NOT NULL,在这种情况下,MySQL扫描t1表,并且使用t1.id查找t2中id相等的数据,如果在t2中找到与t1中id相等的行,MySQL知道t2.id从来不会为NULL, 因此不会再继续扫描t2表中id相等的值。换句话说,对于t1的每一行数据, MySQL仅仅需要找到t2中与之id相同的一条数据即可,不论t2中有多少条与t1中id相同的数据
【完】
转载地址:http://gbsyz.baihongyu.com/