1.Explain含义 {#1.explain%E5%90%AB%E4%B9%89}
Explain是 SQL 分析工具中非常重要的一个功能,它可以模拟优化器执行查询语句,帮助我们理解查询是如何执行的;分析查询执行计划可以帮助我们发现查询瓶颈,优化查询性能。
2.Explain作用 {#2.explain%E4%BD%9C%E7%94%A8}
-
表的读取顺序
-
SQL执行时查询操作类型
-
可以使用哪些索引
-
实际使用哪些索引
-
每张表有多少行记录被扫描
-
SQL语句性能分析
3.Explain用法 {#3.explain%E7%94%A8%E6%B3%95}
数据准备
drop table orders;
drop table products;
drop table users;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
password VARCHAR(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
price FLOAT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
order_date DATETIME NOT NULL,
total_price FLOAT NOT NULL,
product_id INT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (product_id) REFERENCES products(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
alter table users add index index_name_email (name,email);
INSERT INTO users (name, email, password)
VALUES ('张三', 'zhangsan@example.com', 'password123'),
('李四', 'lisi@example.com', 'password123'),
('王五', 'wangwu@example.com', 'password123'),
('赵六', 'zhaoli@example.com', 'password123'),
('钱七', 'qianqi@example.com', 'password123');
INSERT INTO products (name, price)
VALUES ('产品 1', 10.00),
('产品 2', 15.00),
('产品 3', 20.00),
('产品 4', 12.00),
('产品 5', 18.00);
`INSERT INTO orders (user_id, order_date, total_price, product_id)`
`
VALUES (1, '2023-02-18 10:00:00', 100.00, 1),`
`
(2, '2023-02-18 11:00:00', 50.00, 2),`
`
(3, '2023-02-18 12:00:00', 20.00, 3),`
`
(4, '2023-02-18 13:00:00', 15.00, 4),`
`
(5, '2023-02-18 14:00:00', 25.00, 5); `
MySQL5.7版本之前,使用Explain Partitions在Explain的基础上额外多返回partitions列;
Explain Partitions select * from users;
MySQL5.7版本引入了这两个特性,直接使用Explain关键字可以将partitions列、filtered列、extra列直接查询出来。
Explain select * from users;
Explain语句返回列的各列含义:
|---------------|-------------------------------| | id | 每个select都有一个对应的id号,并且是从1开始自增的 | | select_type | 查询语句执行的查询操作类型 | | table | 表名 | | partitions | 表分区情况 | | type | 查询所用的访问类型 | | possible_keys | 可能用到的索引 | | key | 实际查询用到的索引 | | key_len | 所使用到的索引长度 | | ref | 使用到索引时,与索引进行等值匹配的列或者常量 | | rows | 预计扫描的行数(索引行数或者表记录行数) | | filtered | 表示符合查询条件的数据百分比 | | Extra | SQL执行的额外信息 |
这些查询列大家先留一个印象,后续会详细讲解。
4.Explain返回列详解 {#4.explain%E8%BF%94%E5%9B%9E%E5%88%97%E8%AF%A6%E8%A7%A3}
接下来我们将展示Explain中每个列的信息
1. id列:每个select都有一个对应的id号,并且是从1开始自增的。 {#1.-id%E5%88%97%EF%BC%9A%E6%AF%8F%E4%B8%AAselect%E9%83%BD%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AF%B9%E5%BA%94%E7%9A%84id%E5%8F%B7%EF%BC%8C%E5%B9%B6%E4%B8%94%E6%98%AF%E4%BB%8E1%E5%BC%80%E5%A7%8B%E8%87%AA%E5%A2%9E%E7%9A%84%E3%80%82}
●如果id序号相同,从上往下执行。
●如果id序号不同,序号大先执行。
●如果两种都存在,先执行序号大,在同级从上往下执行。
●如果显示NULL,最后执行。表示结果集,并且不需要使用它来进行查询。
id序号相同
explain
SELECT users.name, orders.total_price, products.price
FROM users
INNER JOIN orders ON users.id = orders.user_id
INNER JOIN products ON orders.product_id = products.id;
id序号不同
explain
select * from orders where product_id = (select id from products where products.price = 10);
两种都存在
set session optimizer_switch='derived_merge=off'; #关闭MySQL5.7对衍生表合并优化
explain
select orders.\*
from (select id from products) as temp inner join orders on temp.id = orders.product_id;
`set session optimizer_switch='derived_merge=on'; #还原配置`
显示NULL
explain
select id from users
union
select id from products;
优化器会针对子查询进行一定的优化重写SQL:
EXPLAIN select * from users WHERE id in (select user_id from orders where id = 1);
show WARNINGS;
2.select_type列:表示查询语句执行的查询操作类型 {#2.select_type%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%E6%89%A7%E8%A1%8C%E7%9A%84%E6%9F%A5%E8%AF%A2%E6%93%8D%E4%BD%9C%E7%B1%BB%E5%9E%8B}
2.1.simple:简单select,不包括union与子查询 {#2.1.simple%EF%BC%9A%E7%AE%80%E5%8D%95select%EF%BC%8C%E4%B8%8D%E5%8C%85%E6%8B%ACunion%E4%B8%8E%E5%AD%90%E6%9F%A5%E8%AF%A2}
Explain select * from users;
连接查询
Explain select * from users inner join orders on users.id = orders.user_id;
2.2.primary:复杂查询中最外层查询,比如使用union或union all时,id为1的记录select_type通常是primary {#2.2.primary%EF%BC%9A%E5%A4%8D%E6%9D%82%E6%9F%A5%E8%AF%A2%E4%B8%AD%E6%9C%80%E5%A4%96%E5%B1%82%E6%9F%A5%E8%AF%A2%EF%BC%8C%E6%AF%94%E5%A6%82%E4%BD%BF%E7%94%A8union%E6%88%96union-all%E6%97%B6%EF%BC%8Cid%E4%B8%BA1%E7%9A%84%E8%AE%B0%E5%BD%95select_type%E9%80%9A%E5%B8%B8%E6%98%AFprimary}
explain
select id from users
union
select id from products;
2.3.subquery:指在 select 语句中出现的子查询语句,结果不依赖于外部查询(不在from语句中) {#2.3.subquery%EF%BC%9A%E6%8C%87%E5%9C%A8-select-%E8%AF%AD%E5%8F%A5%E4%B8%AD%E5%87%BA%E7%8E%B0%E7%9A%84%E5%AD%90%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%2C%E7%BB%93%E6%9E%9C%E4%B8%8D%E4%BE%9D%E8%B5%96%E4%BA%8E%E5%A4%96%E9%83%A8%E6%9F%A5%E8%AF%A2%EF%BC%88%E4%B8%8D%E5%9C%A8from%E8%AF%AD%E5%8F%A5%E4%B8%AD%EF%BC%89}
explain
select orders.*,(select name from products where id = 1) from orders;
2.4.dependent subquery:指在 select 语句中出现的查询语句,结果依赖于外部查询 {#2.4.dependent-subquery%EF%BC%9A%E6%8C%87%E5%9C%A8-select-%E8%AF%AD%E5%8F%A5%E4%B8%AD%E5%87%BA%E7%8E%B0%E7%9A%84%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%EF%BC%8C%E7%BB%93%E6%9E%9C%E4%BE%9D%E8%B5%96%E4%BA%8E%E5%A4%96%E9%83%A8%E6%9F%A5%E8%AF%A2}
explain
select orders.*,(select name from products where products.id = orders.user_id) from orders;
2.5.derived:派生表,在FROM子句的查询语句,表示从外部数据源中推导出来的,而不是从 SELECT 语句中的其他列中选择出来的。 {#2.5.derived%EF%BC%9A%E6%B4%BE%E7%94%9F%E8%A1%A8%EF%BC%8C%E5%9C%A8from%E5%AD%90%E5%8F%A5%E7%9A%84%E6%9F%A5%E8%AF%A2%E8%AF%AD%E5%8F%A5%EF%BC%8C%E8%A1%A8%E7%A4%BA%E4%BB%8E%E5%A4%96%E9%83%A8%E6%95%B0%E6%8D%AE%E6%BA%90%E4%B8%AD%E6%8E%A8%E5%AF%BC%E5%87%BA%E6%9D%A5%E7%9A%84%EF%BC%8C%E8%80%8C%E4%B8%8D%E6%98%AF%E4%BB%8E-select-%E8%AF%AD%E5%8F%A5%E4%B8%AD%E7%9A%84%E5%85%B6%E4%BB%96%E5%88%97%E4%B8%AD%E9%80%89%E6%8B%A9%E5%87%BA%E6%9D%A5%E7%9A%84%E3%80%82}
set session optimizer_switch='derived_merge=off'; #关闭MySQL5.7对衍生表合并优化
explain
select \* from (select user_id from orders where id = 1) as temp;
`set session optimizer_switch='derived_merge=on'; #还原配置`
2.6.union:分union与union all两种,若第二个select出现在union之后,则被标记为union;如果union被from子句的子查询包含,那么第一个select会被标记为derived;union会针对相同的结果集进行去重,union all不会进行去重处理。 {#2.6.union%EF%BC%9A%E5%88%86union%E4%B8%8Eunion-all%E4%B8%A4%E7%A7%8D%EF%BC%8C%E8%8B%A5%E7%AC%AC%E4%BA%8C%E4%B8%AAselect%E5%87%BA%E7%8E%B0%E5%9C%A8union%E4%B9%8B%E5%90%8E%EF%BC%8C%E5%88%99%E8%A2%AB%E6%A0%87%E8%AE%B0%E4%B8%BAunion%EF%BC%9B%E5%A6%82%E6%9E%9Cunion%E8%A2%ABfrom%E5%AD%90%E5%8F%A5%E7%9A%84%E5%AD%90%E6%9F%A5%E8%AF%A2%E5%8C%85%E5%90%AB%EF%BC%8C%E9%82%A3%E4%B9%88%E7%AC%AC%E4%B8%80%E4%B8%AAselect%E4%BC%9A%E8%A2%AB%E6%A0%87%E8%AE%B0%E4%B8%BAderived%EF%BC%9Bunion%E4%BC%9A%E9%92%88%E5%AF%B9%E7%9B%B8%E5%90%8C%E7%9A%84%E7%BB%93%E6%9E%9C%E9%9B%86%E8%BF%9B%E8%A1%8C%E5%8E%BB%E9%87%8D%EF%BC%8Cunion-all%E4%B8%8D%E4%BC%9A%E8%BF%9B%E8%A1%8C%E5%8E%BB%E9%87%8D%E5%A4%84%E7%90%86%E3%80%82}
explain
select * from (
select id from products where price = 10
union
select id from orders where user_id in (1,2)
union
select id from users where name = '张三' ) as temp;
union all
explain
select * from (
select id from products where price = 10
union all
select id from orders where user_id in (1,2)
union all
select id from users where name = '张三' ) as temp;
2.7.dependent union:当union作为子查询时,其中第一个union为dependent subquery,第二个union为dependent union。 {#2.7.dependent-union%EF%BC%9A%E5%BD%93union%E4%BD%9C%E4%B8%BA%E5%AD%90%E6%9F%A5%E8%AF%A2%E6%97%B6%EF%BC%8C%E5%85%B6%E4%B8%AD%E7%AC%AC%E4%B8%80%E4%B8%AAunion%E4%B8%BAdependent-subquery%EF%BC%8C%E7%AC%AC%E4%BA%8C%E4%B8%AAunion%E4%B8%BAdependent-union%E3%80%82}
explain
select * from orders where id in (
select id from products where price = 10
union
select id from orders where user_id = 2
union
select id from users where name = '张三' );
2.8.union result:如果两个查询中有相同的列,则会对这些列进行重复删除,只保留一个表中的列。 {#2.8.union-result%EF%BC%9A%E5%A6%82%E6%9E%9C%E4%B8%A4%E4%B8%AA%E6%9F%A5%E8%AF%A2%E4%B8%AD%E6%9C%89%E7%9B%B8%E5%90%8C%E7%9A%84%E5%88%97%EF%BC%8C%E5%88%99%E4%BC%9A%E5%AF%B9%E8%BF%99%E4%BA%9B%E5%88%97%E8%BF%9B%E8%A1%8C%E9%87%8D%E5%A4%8D%E5%88%A0%E9%99%A4%EF%BC%8C%E5%8F%AA%E4%BF%9D%E7%95%99%E4%B8%80%E4%B8%AA%E8%A1%A8%E4%B8%AD%E7%9A%84%E5%88%97%E3%80%82}
explain
select id from users
union
select id from products;
3.table列:查询所涉及的表名。如果有多个表,将显示多行记录 {#3.table%E5%88%97%EF%BC%9A%E6%9F%A5%E8%AF%A2%E6%89%80%E6%B6%89%E5%8F%8A%E7%9A%84%E8%A1%A8%E5%90%8D%E3%80%82%E5%A6%82%E6%9E%9C%E6%9C%89%E5%A4%9A%E4%B8%AA%E8%A1%A8%EF%BC%8C%E5%B0%86%E6%98%BE%E7%A4%BA%E5%A4%9A%E8%A1%8C%E8%AE%B0%E5%BD%95}
4.partitions列:表分区情况 {#4.partitions%E5%88%97%EF%BC%9A%E8%A1%A8%E5%88%86%E5%8C%BA%E6%83%85%E5%86%B5}
查询语句所涉及的表的分区情况。具体来说,它会显示出查询语句在哪些分区上执行,以及是否使用了分区裁剪等信息。如果没有分区,该项为NULL。
5.type列:查询所使用的访问类型 {#5.type%E5%88%97%EF%BC%9A%E6%9F%A5%E8%AF%A2%E6%89%80%E4%BD%BF%E7%94%A8%E7%9A%84%E8%AE%BF%E9%97%AE%E7%B1%BB%E5%9E%8B}
效率从高到低分别为:
system > const > eq_ref > ref > fulltext > ref_or_null > range > index > ALL, 一般来说保证range 级别,最好能达到ref级别。
5.1.system:const类型的一种特殊场景,查询的表只有一行记录的情况,并且该表使用的存储引擎的统计数据是精确的 {#5.1.system%EF%BC%9Aconst%E7%B1%BB%E5%9E%8B%E7%9A%84%E4%B8%80%E7%A7%8D%E7%89%B9%E6%AE%8A%E5%9C%BA%E6%99%AF%EF%BC%8C%E6%9F%A5%E8%AF%A2%E7%9A%84%E8%A1%A8%E5%8F%AA%E6%9C%89%E4%B8%80%E8%A1%8C%E8%AE%B0%E5%BD%95%E7%9A%84%E6%83%85%E5%86%B5%EF%BC%8C%E5%B9%B6%E4%B8%94%E8%AF%A5%E8%A1%A8%E4%BD%BF%E7%94%A8%E7%9A%84%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E%E7%9A%84%E7%BB%9F%E8%AE%A1%E6%95%B0%E6%8D%AE%E6%98%AF%E7%B2%BE%E7%A1%AE%E7%9A%84}
InnoDb存储引擎的统计数据不是精确的,虽然只有一条数据但是type类型为ALL;
DROP TABLE t;
CREATE TABLE t(i INT) ENGINE=InnoDb;
INSERT INTO t VALUES(1);
explain select * from t;
Memory存储引擎的统计数据是精确的,所以当只有一条记录的时候type类型为system。
DROP TABLE tt;
CREATE TABLE tt(i INT) ENGINE=memory;
INSERT INTO tt VALUES(1);
explain select * from tt;
5.2.const:基于主键或唯一索引查看一行,当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问转换成常量查询,效率高 {#5.2.const%EF%BC%9A%E5%9F%BA%E4%BA%8E%E4%B8%BB%E9%94%AE%E6%88%96%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E6%9F%A5%E7%9C%8B%E4%B8%80%E8%A1%8C%EF%BC%8C%E5%BD%93mysql%E5%AF%B9%E6%9F%A5%E8%AF%A2%E6%9F%90%E9%83%A8%E5%88%86%E8%BF%9B%E8%A1%8C%E4%BC%98%E5%8C%96%EF%BC%8C%E5%B9%B6%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%B8%80%E4%B8%AA%E5%B8%B8%E9%87%8F%E6%97%B6%EF%BC%8C%E4%BD%BF%E7%94%A8%E8%BF%99%E4%BA%9B%E7%B1%BB%E5%9E%8B%E8%AE%BF%E9%97%AE%E8%BD%AC%E6%8D%A2%E6%88%90%E5%B8%B8%E9%87%8F%E6%9F%A5%E8%AF%A2%EF%BC%8C%E6%95%88%E7%8E%87%E9%AB%98}
explain
select * from orders where id = 1;
5.3.eq_ref:基于主键或唯一索引连接两个表,对于每个索引键值,只有一条匹配记录,被驱动表的类型为'eq_ref' {#5.3.eq_ref%EF%BC%9A%E5%9F%BA%E4%BA%8E%E4%B8%BB%E9%94%AE%E6%88%96%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E8%BF%9E%E6%8E%A5%E4%B8%A4%E4%B8%AA%E8%A1%A8%EF%BC%8C%E5%AF%B9%E4%BA%8E%E6%AF%8F%E4%B8%AA%E7%B4%A2%E5%BC%95%E9%94%AE%E5%80%BC%EF%BC%8C%E5%8F%AA%E6%9C%89%E4%B8%80%E6%9D%A1%E5%8C%B9%E9%85%8D%E8%AE%B0%E5%BD%95%EF%BC%8C%E8%A2%AB%E9%A9%B1%E5%8A%A8%E8%A1%A8%E7%9A%84%E7%B1%BB%E5%9E%8B%E4%B8%BA'eq_ref'}
explain
select users.* from users inner join orders on users.id = orders.id;
5.4.ref:基于非唯一索引连接两个表或通过二级索引列与常量进行等值匹配,可能会存在多条匹配记录 {#5.4.ref%EF%BC%9A%E5%9F%BA%E4%BA%8E%E9%9D%9E%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E8%BF%9E%E6%8E%A5%E4%B8%A4%E4%B8%AA%E8%A1%A8%E6%88%96%E9%80%9A%E8%BF%87%E4%BA%8C%E7%BA%A7%E7%B4%A2%E5%BC%95%E5%88%97%E4%B8%8E%E5%B8%B8%E9%87%8F%E8%BF%9B%E8%A1%8C%E7%AD%89%E5%80%BC%E5%8C%B9%E9%85%8D%EF%BC%8C%E5%8F%AF%E8%83%BD%E4%BC%9A%E5%AD%98%E5%9C%A8%E5%A4%9A%E6%9D%A1%E5%8C%B9%E9%85%8D%E8%AE%B0%E5%BD%95}
1.关联查询,使用非唯一索引进行匹配。
explain
select users.* from users inner join orders on users.id = orders.user_id;
2.简单查询,使用二级索引列匹配。
explain
select * from orders where user_id = 1;
5.5.range:使用非唯一索引扫描部分索引,比如使用索引获取某些范围区间的记录 {#5.5.range%EF%BC%9A%E4%BD%BF%E7%94%A8%E9%9D%9E%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E6%89%AB%E6%8F%8F%E9%83%A8%E5%88%86%E7%B4%A2%E5%BC%95%EF%BC%8C%E6%AF%94%E5%A6%82%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%E8%8E%B7%E5%8F%96%E6%9F%90%E4%BA%9B%E8%8C%83%E5%9B%B4%E5%8C%BA%E9%97%B4%E7%9A%84%E8%AE%B0%E5%BD%95}
explain
select * from orders where user_id > 3;
5.6.index:扫描整个索引就能拿到结果,一般是二级索引,这种查询一般为使用覆盖索引(需优化,缩小数据范围) {#5.6.index%EF%BC%9A%E6%89%AB%E6%8F%8F%E6%95%B4%E4%B8%AA%E7%B4%A2%E5%BC%95%E5%B0%B1%E8%83%BD%E6%8B%BF%E5%88%B0%E7%BB%93%E6%9E%9C%EF%BC%8C%E4%B8%80%E8%88%AC%E6%98%AF%E4%BA%8C%E7%BA%A7%E7%B4%A2%E5%BC%95%EF%BC%8C%E8%BF%99%E7%A7%8D%E6%9F%A5%E8%AF%A2%E4%B8%80%E8%88%AC%E4%B8%BA%E4%BD%BF%E7%94%A8%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95%EF%BC%88%E9%9C%80%E4%BC%98%E5%8C%96%EF%BC%8C%E7%BC%A9%E5%B0%8F%E6%95%B0%E6%8D%AE%E8%8C%83%E5%9B%B4%EF%BC%89}
explain
select user_id from orders;
5.7.all:扫描整个表进行匹配,即扫描聚簇索引树(需优化,添加索引优化) {#5.7.all%EF%BC%9A%E6%89%AB%E6%8F%8F%E6%95%B4%E4%B8%AA%E8%A1%A8%E8%BF%9B%E8%A1%8C%E5%8C%B9%E9%85%8D%EF%BC%8C%E5%8D%B3%E6%89%AB%E6%8F%8F%E8%81%9A%E7%B0%87%E7%B4%A2%E5%BC%95%E6%A0%91%EF%BC%88%E9%9C%80%E4%BC%98%E5%8C%96%EF%BC%8C%E6%B7%BB%E5%8A%A0%E7%B4%A2%E5%BC%95%E4%BC%98%E5%8C%96%EF%BC%89}
explain
select * from users;
5.8.NULL:MySQL在优化过程中分解语句就已经可以获取到结果,执行时甚至不用访问表或索引。 {#5.8.null%EF%BC%9Amysql%E5%9C%A8%E4%BC%98%E5%8C%96%E8%BF%87%E7%A8%8B%E4%B8%AD%E5%88%86%E8%A7%A3%E8%AF%AD%E5%8F%A5%E5%B0%B1%E5%B7%B2%E7%BB%8F%E5%8F%AF%E4%BB%A5%E8%8E%B7%E5%8F%96%E5%88%B0%E7%BB%93%E6%9E%9C%EF%BC%8C%E6%89%A7%E8%A1%8C%E6%97%B6%E7%94%9A%E8%87%B3%E4%B8%8D%E7%94%A8%E8%AE%BF%E9%97%AE%E8%A1%A8%E6%88%96%E7%B4%A2%E5%BC%95%E3%80%82}
explain
select min(id) from users;
6.possible_keys列:表示在查询中可能使用到某个索引或多个索引;如果没有选择索引,显示NULL {#6.possible_keys%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E5%9C%A8%E6%9F%A5%E8%AF%A2%E4%B8%AD%E5%8F%AF%E8%83%BD%E4%BD%BF%E7%94%A8%E5%88%B0%E6%9F%90%E4%B8%AA%E7%B4%A2%E5%BC%95%E6%88%96%E5%A4%9A%E4%B8%AA%E7%B4%A2%E5%BC%95%EF%BC%9B%E5%A6%82%E6%9E%9C%E6%B2%A1%E6%9C%89%E9%80%89%E6%8B%A9%E7%B4%A2%E5%BC%95%EF%BC%8C%E6%98%BE%E7%A4%BAnull}
7.key列:表示在查询中实际使用的索引,如果没有使用索引,显示NULL。 {#7.key%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E5%9C%A8%E6%9F%A5%E8%AF%A2%E4%B8%AD%E5%AE%9E%E9%99%85%E4%BD%BF%E7%94%A8%E7%9A%84%E7%B4%A2%E5%BC%95%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%B2%A1%E6%9C%89%E4%BD%BF%E7%94%A8%E7%B4%A2%E5%BC%95%EF%BC%8C%E6%98%BE%E7%A4%BAnull%E3%80%82}
8.key_len列:表示当优化器决定使用某个索引执行查询时,该索引记录的最大长度(主要使用在联合索引) {#8.key_len%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E5%BD%93%E4%BC%98%E5%8C%96%E5%99%A8%E5%86%B3%E5%AE%9A%E4%BD%BF%E7%94%A8%E6%9F%90%E4%B8%AA%E7%B4%A2%E5%BC%95%E6%89%A7%E8%A1%8C%E6%9F%A5%E8%AF%A2%E6%97%B6%EF%BC%8C%E8%AF%A5%E7%B4%A2%E5%BC%95%E8%AE%B0%E5%BD%95%E7%9A%84%E6%9C%80%E5%A4%A7%E9%95%BF%E5%BA%A6%EF%BC%88%E4%B8%BB%E8%A6%81%E4%BD%BF%E7%94%A8%E5%9C%A8%E8%81%94%E5%90%88%E7%B4%A2%E5%BC%95%EF%BC%89}
联合索引可以通过这个值算出具体使用了索引中的哪些列。
使用单例索引:
explain
select * from users where id = 1;
使用联合索引:
explain
select * from users where name = '张三' and email = 'zhangsan@example.com';
计算规则:
- 字符串:
char(n):n个字节
varchar(n):如果是uft-8:3n+2字节,加的2个字节存储字符串长度。如果是utf8mb4:4n+2字节。
- 数值类型:
tinyint:1字节
smaillint:2字节
int:4字节
bigint:8字节
- 时间类型:
date:3字节
timestamp:4字节
datetime:8字节
字段如果为NULL,需要1个字节记录是否为NULL
9.ref列:表示将哪个字段或常量和key列所使用的字段进行比较。 {#9.ref%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E5%B0%86%E5%93%AA%E4%B8%AA%E5%AD%97%E6%AE%B5%E6%88%96%E5%B8%B8%E9%87%8F%E5%92%8Ckey%E5%88%97%E6%89%80%E4%BD%BF%E7%94%A8%E7%9A%84%E5%AD%97%E6%AE%B5%E8%BF%9B%E8%A1%8C%E6%AF%94%E8%BE%83%E3%80%82}
当使用索引列等值查询时,与索引列进行等值匹配的对象信息。
1.常量:
explain
select * from users where name = '张三' and email = 'zhangsan@example.com';
2.字段:
explain
select users.* from users inner join orders on users.id = orders.id;
3.函数
explain
select users.* from users inner join orders on users.id = trim(orders.id);
10.rows列:全表扫描时表示需要扫描表的行数估计值;索引扫描时表示扫描索引的行数估计值;值越小越好(不是结果集中的行数) {#10.rows%E5%88%97%EF%BC%9A%E5%85%A8%E8%A1%A8%E6%89%AB%E6%8F%8F%E6%97%B6%E8%A1%A8%E7%A4%BA%E9%9C%80%E8%A6%81%E6%89%AB%E6%8F%8F%E8%A1%A8%E7%9A%84%E8%A1%8C%E6%95%B0%E4%BC%B0%E8%AE%A1%E5%80%BC%EF%BC%9B%E7%B4%A2%E5%BC%95%E6%89%AB%E6%8F%8F%E6%97%B6%E8%A1%A8%E7%A4%BA%E6%89%AB%E6%8F%8F%E7%B4%A2%E5%BC%95%E7%9A%84%E8%A1%8C%E6%95%B0%E4%BC%B0%E8%AE%A1%E5%80%BC%EF%BC%9B%E5%80%BC%E8%B6%8A%E5%B0%8F%E8%B6%8A%E5%A5%BD%EF%BC%88%E4%B8%8D%E6%98%AF%E7%BB%93%E6%9E%9C%E9%9B%86%E4%B8%AD%E7%9A%84%E8%A1%8C%E6%95%B0%EF%BC%89}
1.全表扫描
explain
select * from orders where user_id >= 3 and total_price = 25;
2.索引扫描
explain
select * from orders where user_id > 3;
11.filtered列:表示符合查询条件的数据百分比。可以使用rows * filtered/100计算出与explain前一个表进行连接的行数。 {#11.filtered%E5%88%97%EF%BC%9A%E8%A1%A8%E7%A4%BA%E7%AC%A6%E5%90%88%E6%9F%A5%E8%AF%A2%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E7%99%BE%E5%88%86%E6%AF%94%E3%80%82%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8rows-*-filtered%2F100%E8%AE%A1%E7%AE%97%E5%87%BA%E4%B8%8Eexplain%E5%89%8D%E4%B8%80%E4%B8%AA%E8%A1%A8%E8%BF%9B%E8%A1%8C%E8%BF%9E%E6%8E%A5%E7%9A%84%E8%A1%8C%E6%95%B0%E3%80%82}
前一个表指 explain 中的id值比当前表id值小的表,id相同的时候指后执行的表。
explain
select users.* from users inner join orders on users.id = orders.id;
12.Extra列:SQL执行查询的一些额外信息 {#12.extra%E5%88%97%EF%BC%9Asql%E6%89%A7%E8%A1%8C%E6%9F%A5%E8%AF%A2%E7%9A%84%E4%B8%80%E4%BA%9B%E9%A2%9D%E5%A4%96%E4%BF%A1%E6%81%AF}
12.1.Using Index:使用非主键索引树就可以查询所需要的数据。一般是覆盖索引,即查询列都包含在辅助索引树叶子节点中,不需要回表查询。 {#12.1.using-index%EF%BC%9A%E4%BD%BF%E7%94%A8%E9%9D%9E%E4%B8%BB%E9%94%AE%E7%B4%A2%E5%BC%95%E6%A0%91%E5%B0%B1%E5%8F%AF%E4%BB%A5%E6%9F%A5%E8%AF%A2%E6%89%80%E9%9C%80%E8%A6%81%E7%9A%84%E6%95%B0%E6%8D%AE%E3%80%82%E4%B8%80%E8%88%AC%E6%98%AF%E8%A6%86%E7%9B%96%E7%B4%A2%E5%BC%95%EF%BC%8C%E5%8D%B3%E6%9F%A5%E8%AF%A2%E5%88%97%E9%83%BD%E5%8C%85%E5%90%AB%E5%9C%A8%E8%BE%85%E5%8A%A9%E7%B4%A2%E5%BC%95%E6%A0%91%E5%8F%B6%E5%AD%90%E8%8A%82%E7%82%B9%E4%B8%AD%EF%BC%8C%E4%B8%8D%E9%9C%80%E8%A6%81%E5%9B%9E%E8%A1%A8%E6%9F%A5%E8%AF%A2%E3%80%82}
explain
select user_id,id from orders where user_id = 1;
12.2.Using where:不通过索引查询所需要的数据 {#12.2.using-where%EF%BC%9A%E4%B8%8D%E9%80%9A%E8%BF%87%E7%B4%A2%E5%BC%95%E6%9F%A5%E8%AF%A2%E6%89%80%E9%9C%80%E8%A6%81%E7%9A%84%E6%95%B0%E6%8D%AE}
explain
select * from orders where total_price = 100;
`explain
select * from orders where user_id = 1 and total_price = 100;`
12.3.Using index condition:表示查询列不被索引覆盖,where 条件中是一个索引范围查找,过滤完索引后回表找到所有符合条件的数据行。 {#12.3.using-index-condition%EF%BC%9A%E8%A1%A8%E7%A4%BA%E6%9F%A5%E8%AF%A2%E5%88%97%E4%B8%8D%E8%A2%AB%E7%B4%A2%E5%BC%95%E8%A6%86%E7%9B%96%EF%BC%8Cwhere-%E6%9D%A1%E4%BB%B6%E4%B8%AD%E6%98%AF%E4%B8%80%E4%B8%AA%E7%B4%A2%E5%BC%95%E8%8C%83%E5%9B%B4%E6%9F%A5%E6%89%BE%EF%BC%8C%E8%BF%87%E6%BB%A4%E5%AE%8C%E7%B4%A2%E5%BC%95%E5%90%8E%E5%9B%9E%E8%A1%A8%E6%89%BE%E5%88%B0%E6%89%80%E6%9C%89%E7%AC%A6%E5%90%88%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E8%A1%8C%E3%80%82}
explain
select * from orders where user_id > 3;
12.4.Using temporary:表示需要使用临时表来处理查询; {#12.4.using-temporary%EF%BC%9A%E8%A1%A8%E7%A4%BA%E9%9C%80%E8%A6%81%E4%BD%BF%E7%94%A8%E4%B8%B4%E6%97%B6%E8%A1%A8%E6%9D%A5%E5%A4%84%E7%90%86%E6%9F%A5%E8%AF%A2%EF%BC%9B}
1.total_price列无索引,需要创建一张临时表进行去重
explain
select distinct total_price from orders;
2.name列有联合索引
explain
select distinct name from users;
12.5.Using filesort:当查询中包含 order by 操作而且无法利用索引完成的排序操作,数据较少时从内存排序,如果数据较多需要在磁盘中排序。 需优化成索引排序。 {#12.5.using-filesort%EF%BC%9A%E5%BD%93%E6%9F%A5%E8%AF%A2%E4%B8%AD%E5%8C%85%E5%90%AB-order-by-%E6%93%8D%E4%BD%9C%E8%80%8C%E4%B8%94%E6%97%A0%E6%B3%95%E5%88%A9%E7%94%A8%E7%B4%A2%E5%BC%95%E5%AE%8C%E6%88%90%E7%9A%84%E6%8E%92%E5%BA%8F%E6%93%8D%E4%BD%9C%EF%BC%8C%E6%95%B0%E6%8D%AE%E8%BE%83%E5%B0%91%E6%97%B6%E4%BB%8E%E5%86%85%E5%AD%98%E6%8E%92%E5%BA%8F%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%95%B0%E6%8D%AE%E8%BE%83%E5%A4%9A%E9%9C%80%E8%A6%81%E5%9C%A8%E7%A3%81%E7%9B%98%E4%B8%AD%E6%8E%92%E5%BA%8F%E3%80%82-%E9%9C%80%E4%BC%98%E5%8C%96%E6%88%90%E7%B4%A2%E5%BC%95%E6%8E%92%E5%BA%8F%E3%80%82}
1.total_price列无索引,无法通过索引进行排序。需要先保存total_price与对应的主键id,然后在排序total_price查找数据。
explain
select total_price from orders order by total_price;
2.name列有索引,因索引已经是排好序的所以直接读取就可以了。
explain
select name from users order by name;
12.6.Select tables optimized away:使用某些聚合函数(min,max)来访问某个索引值。 {#12.6.select-tables-optimized-away%EF%BC%9A%E4%BD%BF%E7%94%A8%E6%9F%90%E4%BA%9B%E8%81%9A%E5%90%88%E5%87%BD%E6%95%B0%EF%BC%88min%2Cmax%EF%BC%89%E6%9D%A5%E8%AE%BF%E9%97%AE%E6%9F%90%E4%B8%AA%E7%B4%A2%E5%BC%95%E5%80%BC%E3%80%82}
explain
select min(id) from users;
`explain
select min(password) from users;`
- 总结 {#5.-%E6%80%BB%E7%BB%93}
正确合理使用 MySQL explain 可以帮助我们更好地理解查询执行计划,并确定如何最好地优化查询SQL,提升SQL性能,增加系统稳定性。