SQL对于大多数程序员来说都不陌生,甚至可以与我们使用的具体编程语言(JAVA、C、C++等)相提并论。我们无法总是写出完美的代码,同样我们也无法总是写出完美的SQL。这时候就需要mysql优化器登场了。mysql的优化器会根据表、列、索引的详细信息以及where字句中的条件,对用户下发的SQL进行优化,使其可以更高效地执行。而优化器执行的最有效的一组查询操作就叫做查询执行计划。通过查看执行计划我们不仅可以了解SQL执行的详情,还可以发现一些低效的操作,并以此为依据来改进SQL语句,实现高效查询。当然了,执行计划不仅可以用来查看SELECT语句,同样也可以用来查看DELETE、INSERT、REPLACE和UPDATE语句。由于平时我们使用SELECT最多,而且对于SELECT语句,执行计划可以提供很多关于执行的信息,所以我们主要讨论有关SELECT的执行计划。

环境准备

CREATE TABLE `student` (
  `id`   INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(20) NOT NULL DEFAULT '',
  `age`  TINYINT UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `name_index` (`name`)
)ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO student (name, age) VALUES ('a', 99);
INSERT INTO student (name, age) VALUES ('b', 21);
INSERT INTO student (name, age) VALUES ('c', 23);
INSERT INTO student (name, age) VALUES ('d', 50);
INSERT INTO student (name, age) VALUES ('e', 15);

CREATE TABLE  `sc` (
  `sid` INT NOT NULL,
  `course` VARBINARY(10) NOT NULL DEFAULT '',
  `score` DECIMAL(4, 1) NOT NULL DEFAULT 0,
  KEY (sid, course)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

INSERT INTO sc (sid, course, score) values('1', '语文', 34);
INSERT INTO sc (sid, course, score) values ('1', '数学', 78);
INSERT INTO sc (sid, course, score) values('2', '语文', 34);
INSERT INTO sc (sid, course, score) values ('2', '英语', 56);
INSERT INTO sc (sid, course, score) values('3', '数学', 34);
INSERT INTO sc (sid, course, score) values ('4', '英语', 78);

执行计划详解

Mysql 查看执行计划的多种方式 怎么查看mysql执行计划_Mysql 查看执行计划的多种方式

id

select语句得标识符,每个select都会自动分配一个唯一的标识符

select_type

select查询类型

  • SIMPLE:简单的查询,不包含UNION和子查询
  • PRIMARY:最外层的查询
  • UNION:union语句中第二个或者后面的查询
  • DEPENDENT UNION:union语句中第二个或者后面的查询,取决于外部的查询
  • UNION RESULT:union的结果
  • SUBQUERY:子查询中的第一个查询
  • DEPENDENT SUBQUERY:子查询中的第一个查询,取决于外部查询

table

查询涉及到的表

partitions

查询记录匹配的分区,对于非分区表值为NULL

type

连接类型

  • system:该表的查询结果只有一条,是const的特例
  • const:该表最多一个匹配的行,该行在查询开始的时候读取
    因为只有一行,优化器会将该行中列的值视为常量。因为只读取一次,所以非常快
  • eq_ref
    当使用=运算符进行索引列的比较时使用。比较值可以是常量,也可以是之前读取的表中列的表达式。
  • ref
    对于先前表中的每个行,将从该表中读取所有匹配的行。如果联接仅使用最左前缀、键不是主键、键是非唯一键(换句话说,联接无法根据键值选择单个行)
  • range
    使用索引进行范围查询,通常出现在=,<>,>,>=,<,<=,is null,<=>,between,in()操作中。当type为range时,ref为NULL,并且key_len字段是此次查询使用到的索引最长的那个
  • index
    连接类型和ALL相同,除了扫描索引树。一般来说index比all快,因为索引的数据量一般小于数据
  • all
    进行全表扫描,通常要禁止这种连接。查询效率极低

possible_keys

查询中可能用到的索引,也就是说不一定会使用到这些索引

key

查询实际用到的索引,可能是一个possibley_keys中不存在的索引

key_len

实际使用索引的长度。对于符合索引可以通过此值看出实际使用到了索引的哪些字段。计算规则如下:

  • 字符串:
    char(n):utf8编码为3n字节,utf8mb4编码为4n字节
    varchar(n):utf8编码为3n+2字节,utf8mb4编码为4n+2字节
  • 数值类型
    tinyint:1字节
    smallint:2字节
    mediumint:3字节
    int:4字节
    bigint:8字节
  • 时间类型
    date:3字节
    datetime:8字节
    timestamp:4字节

字段属性:NULL属性占用一个字节,如果一个字段是NOT NULL的,则没有此属性

ref

显示将哪些列或常量与索引进行比较

rows

要查找符合条件的结果需要扫面的行数,此值是估计值。值越小,效率越高

filtered

被过滤的行数的百分表如果是100,表示没有被过滤。例如:如果行数为1000,过滤条件为50.00(50%),则与下表连接的行数为1000×50%= 500。

Extra

  • Using index:覆盖索引扫描,需要的数据在索引中就可以查到,不需要查找数据行,也说明了性能不错
  • Using index condition:索引下推,如果不使用索引下推,则存储引擎将遍历索引以在基表中定位行,并将其返回给mysql服务器,后者将评估where行的条件。启用索引下推后,mysql服务器会将where条件下降到存储引擎,然后存储引擎只读取满足条件的行。索引下推可以减少存储引擎访问基表的次数以及mysql服务器访问存储引擎的次数
  • Using filesort:仅通过索引无法排序,mysql需要额外遍历一次,以找出如何按照排序顺序检索行
  • Using temporary:查询用到了临时表。例如在使用group by和order by的情况下需要列出不同的列

更多信息介绍参考mysql优化之查询执行计划