首页 > 数据库 >id,table列(1)—mysql执行计划(四十七)

id,table列(1)—mysql执行计划(四十七)

时间:2022-11-01 17:40:02浏览次数:42  
标签:s1 key mysql table NULL type id SELECT


前面说了semi-join,这个是在where或者on语句后面,in里面,并且外层的条件必须用and与子查询连接,semi-join的作用就是,不管子查询有多少条数据返回,都不管,外层都只查询出来外层表数据,如果不符合条件,可以用物化表或者in变exists方法优化。还有派生表查询,可以内外合并,不行的话就物化查询。

​​Semi-join使用条件,派生表优化 (3)—mysql基于规则优化(四十六)​​

Explain

一条查询语句经过mysql优化器之后,会生成一个执行计划,这个计划展现了接下来具体查询方式,比如多表连接的顺序,mysql贴心的为我们查看提供了explain语句。

mysql> EXPLAIN SELECT 1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 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 |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
1 row in set, 1 warning (0.01 sec)

上面的这个就是执行计划,除了select,我们吧delete,update,insert,都可以用explain查看执行计划,我们先把列熟悉一下。

Id:一个大的查询语句,每个select关键字都对应一个唯一id。

Select_type:select关键字对应的查询类型。

Table:表名。

Partitions:匹配的分区信息。

Type:针对单表的访问方法。

Possible_keys:可能用到的索引。

Key:实际上用到的索引。

Key_len:实际使用的索引长度。

Ref:当使用索引等值查询时,索引列进行等值匹配的对象信息。

Rows:预估需要读取记录条数。

Filted:某个表经过搜索条件过滤后剩余记录条数的百分比。

Extra:一些额外信息。

下面还是用single_table给大家演示,为了方便大家阅读,我们再看一遍表结构:

CREATE TABLE single_table (
id INT NOT NULL AUTO_INCREMENT,
key1 VARCHAR(100),
key2 INT,
key3 VARCHAR(100),
key_part1 VARCHAR(100),
key_part2 VARCHAR(100),
key_part3 VARCHAR(100),
common_field VARCHAR(100),
PRIMARY KEY (id),
KEY idx_key1 (key1),
UNIQUE KEY idx_key2 (key2),
KEY idx_key3 (key3),
KEY idx_key_part(key_part1, key_part2, key_part3)
) Engine=InnoDB CHARSET=utf8;

执行计划输出各列详情

Table

我们先看一个比较简单的查询语句:

mysql> EXPLAIN SELECT * FROM s1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

因为我们是单表查询,可以看到table是s1。

mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
2 rows in set, 1 warning (0.01 sec)

如果是查询两个表,可以看到table是s1和s2。所以由上可以知道,每一列都是代表单表查询的详细数据。

id

我们都知道每次查询都是有一个select的,

SELECT * FROM s1 WHERE key1 = 'a';

但有的时候也会出现多个select的情况,比如union查询和子查询

  • SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2);
  • SELECT * FROM s1 UNION SELECT * FROM s2;

mysql规定,每出现一个select就会多一个id,比如下面的sql:

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | const | 8 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.03 sec)

就只有一个id,对于链接查询的话,from后面可以跟着多个表,每个表都会对应一条记录,但这些记录是相同的:

mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
| 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 1 | SIMPLE | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+---------------------------------------+
2 rows in set, 1 warning (0.01 sec)

上面的结果可以看出,虽然有两条数据,但id是相同的,并且前面的表是驱动表,后面的表是被驱动表,所以从上面可以看出,s1是驱动表,s2是被驱动表。

如果是子查询,则会 每个select对应一个id。

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
| 1 | PRIMARY | s1 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9688 | 100.00 | Using where |
| 2 | SUBQUERY | s2 | NULL | index | idx_key1 | idx_key1 | 303 | NULL | 9954 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+-------------+
2 rows in set, 1 warning (0.02 sec)

可以看到,这两个id是不一样的,但有的时候,子查询会被查询优化器进行重写,然后转成连接查询,

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key3 FROM s2 WHERE common_field = 'a');
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
| 1 | SIMPLE | s2 | NULL | ALL | idx_key3 | NULL | NULL | NULL | 9954 | 10.00 | Using where; Start temporary |
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_key1 | 303 | xiaohaizi.s2.key3 | 1 | 100.00 | End temporary |
+----+-------------+-------+------------+------+---------------+----------+---------+-------------------+------+----------+------------------------------+
2 rows in set, 1 warning (0.00 sec)

 可以看到,虽然有两个select,但两个id都是相同的,但是对于union拉说,还是有点不同

mysql> EXPLAIN SELECT * FROM s1  UNION SELECT * FROM s2;
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | NULL |
| NULL | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+------+---------------+------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

可以看到多了个id为null的列,这是什么呢?是因为union是需要去重的,那他怎么去重呢,就需要创建临时表,所以创建了table为union1,2的临时表,id为null,相对于union all就不需要去重,这种就不会创建临时表

mysql> EXPLAIN SELECT * FROM s1  UNION ALL SELECT * FROM s2;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | PRIMARY | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 9688 | 100.00 | NULL |
| 2 | UNION | s2 | NULL | ALL | NULL | NULL | NULL | NULL | 9954 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------+
2 rows in set, 1 warning (0.01 sec)

标签:s1,key,mysql,table,NULL,type,id,SELECT
From: https://blog.51cto.com/u_15856702/5814521

相关文章

  • Semi-join使用条件,派生表优化 (3)—mysql基于规则优化(四十六)
    前面说了子查询里有no/any/all不能用limit,groupby,orderby等,他会被查询优化器优化掉,子查询可能会物化转成内连接semi-join查询,物化就是会吧子查询看做一个表,如果数据太大,超......
  • 子查询注意事项&semi-join(2)—mysql基于规则优化(四十五)
    前面说了mysql会吧一些冗余的sql语句查询优化重写,比如多于的括号,比如有的外连接其实跟内连接类似,可以优化查询表的顺序。子查询又分为相关和不相关子查询,如果子查询过滤条件......
  • 不重用的undo日志 (2)—mysql进阶(六十五)
    前面我们说了undo日志在insert,update,delete存储的日志格式,delete存储的type是trx_undo_del_mark_rec里面有个参数oldroll_pointer会指向insert的地址值,恢复需要的数据。Sel......
  • undo日志insert,update,delete (1)—mysql进阶(六十四)
    前面说了redo日志为了保证系统宕机的情况下,能够恢复数据,恢复数据是在以checkpoint_lsn为起始位子来恢复,在该值之前的都是已经持久化到磁盘的,可以为了提升效率而放弃,而之后的......
  • redo log(1)—mysql进阶(五十九)
    上篇文章说了我们可以用begin和statrtransaction,提交可以commit,rollback回滚,可以指定回滚到保存点,也可以设置全局变量setautocommitoff。也会隐式提交,比如开启事务后,如......
  • 【MySQL】常见错误汇总
    一、MySQL无法重新启动(cmd)netstopmysql手动关闭mysql后netstartmysql无法重启,提示服务没有报告任何错误直接上网搜启动问题的话可能解决不了,因为原因很多样。所以先......
  • redo log-Transaction(2)—mysql进阶(六十)
    前面我们说了为了吧bufferpool的数据持久化到磁盘上,比如修改了一条数据,不可能每次吧整个页的数据都刷新过去,这样耗费性能,innoDB就是把修改的数据记录在redo日志里,redo日志......
  • springBoot+mysql实现用户权限控制--系统框架搭建(四)
    上篇文章说了AOP实现上下文的存储,有需要的可以看看,​​AOP实现上下文存储---系统框架搭建(三)​​环境需求:springboot+mysql5.7.16+Lombok1.18.121、需求背景为了实......
  • transaction (2)—mysql进阶(五十八)
    上篇文章说了acid四个事务的特性,原子性保证要不两个sql一起执行,要么不执行,隔离性,两个事务之间必须互不干扰,一致性,两边的数据必须保持一致,可以说一致性的前提是原子性和隔离......
  • MySQL中Delete和Truncate区别
    一、清空表语句truncatetable[表名];deletefrom[表名]where…; 二、相同点两者都是删除表数据但不会删除表结构 三、不同点delete支持按条件删除,TRUNCA......