zoukankan      html  css  js  c++  java
  • MySQL Optimizer Analysis——Join types

    MySQL Optimizer Analysis——Join types

    Louis Hust

     

    0  前言

    通过EXPLAIN查看执行计划,其输出中较为重要的列为type列,此列决定了操作此表的类型,在MySQL内部,此列对应了 enum join_type。这里分别针对Manual中列举出来的各种不同的type,进行实例显示,让大家更加明确的了解各个type的实际意义, 有利于加深大家对explain输出的理解。

     

    由于MySQL5.6对Optimizer的代码进行了重构,条理上更加清晰,故本实验的环境是:

    Server version: 5.6.6-m9-debug-log Source distribution
    
    
     

    0  Join Type实例讲解

     

    0.0  system

    Manual上的说法是表只有一行数据即为system类型,说的过于模糊,因为我试过innodb的表(默认引擎),全表查询根本不是system, 这不科学,经过代码的查看,发现并没这么简单,要想join type为system,有两点要求:

    1. 表中数据行数小于等于1
       
    2. 表的存储引擎为MyISAM,HEAP或ARCHIVE
       

    第一条和manual一致,第二条是通过代码查看发现的,只有上述的三种引擎,其ha_table_flag才包含HA_STATS_RECORDS_IS_EXACT, 这个标志表示统计信息中统计的行数是精确的,不是模糊的。此标志决定了system类型的判断,点到为止, 具体的可以查看代码make_join_statistics。下面给出一个实例:

     
    mysql> create table t1(c1 int, c2 int) engine = myisam;
    Query OK, 0 rows affected (0.12 sec)
    
    mysql> insert into t1 values(1,1);
    Query OK, 1 row affected (0.03 sec)
    
    mysql> explain select * from t1;
    +----+-------------+-------+--------+---------------+------+---------+------+------+-------+
    | id | select_type | table | type   | possible_keys | key  | key_len | ref  | rows | Extra |
    +----+-------------+-------+--------+---------------+------+---------+------+------+-------+
    |  1 | SIMPLE      | t1    | system | NULL          | NULL | NULL    | NULL |    1 | NULL  |
    +----+-------------+-------+--------+---------------+------+---------+------+------+-------+
    1 row in set (0.00 sec)
    
    
     

    0.0  const

    此种类型的join type表示最多只有一个匹配的行,优化器判断一个表为const后,会读取相应的行,在之后的优化过程中会直接用读取出的常量代替 语句中引用到的此表的个列。当出现主键或唯一键和常量进行等值比较时,便会产生const。

    mysql> create table t1(c1 int primary key, c2 int unique);
    Query OK, 0 rows affected (0.26 sec)
    
    mysql> insert into t1 values(1,1),(2,2),(3,3);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    mysql> explain select * from t1 where c1=2;
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    |  1 | SIMPLE      | t1    | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
    +----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
    1 row in set (0.00 sec)
    
    mysql> explain select * from t1 where c2=2;
    +----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
    | id | select_type | table | type  | possible_keys | key  | key_len | ref   | rows | Extra       |
    +----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
    |  1 | SIMPLE      | t1    | const | c2            | c2   | 5       | const |    1 | Using index |
    +----+-------------+-------+-------+---------------+------+---------+-------+------+-------------+
    1 row in set (0.01 sec)
    
    
     

    0.0  eq_ref

    eq_ref类型,首先要确定的是它是一种ref,即reference,在ref属性上又添加了等值(eq)的属性,即为eq_ref,这种类型 是较为常见的一种比较好的join type。用于根据primary key或者unique not null索引进行等值判断,当然不再是和const进行比较, 如果和const进行比较,那么就是const类型的表了。 而是和前面一张表的一条记录进行比较(MySQL的join是nest loop类型的,且是left deep树,故表的join是从左到右一条一条记录进行匹配的)。

     
    mysql> create table t1(c1 int, c2 int);
    Query OK, 0 rows affected (0.21 sec)
    
    mysql> insert into t1 values(1,1),(2,2);
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> create table t2(c1 int primary key, c2 int unique not null);
    Query OK, 0 rows affected (0.21 sec)
    
    mysql> insert into t2 values(1,1), (2,2), (3,3);
    Query OK, 3 rows affected (0.00 sec)
    Records: 3  Duplicates: 0  Warnings: 0
    
    mysql> commit;
    Query OK, 0 rows affected (0.12 sec)
    
    mysql> explain select * from t1, t2 where t1.c1=t2.c1;
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+
    | id | select_type | table | type   | possible_keys | key     | key_len | ref       | rows | Extra       |
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL    | NULL          | NULL    | NULL    | NULL      |    2 | Using where |
    |  1 | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | opt.t1.c1 |    1 | NULL        |
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+-------------+
    2 rows in set (0.00 sec)
    
    mysql> explain select * from t1, t2 where t1.c1=t2.c2;
    +----+-------------+-------+--------+---------------+------+---------+-----------+------+-------------+
    | id | select_type | table | type   | possible_keys | key  | key_len | ref       | rows | Extra       |
    +----+-------------+-------+--------+---------------+------+---------+-----------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL    | NULL          | NULL | NULL    | NULL      |    2 | Using where |
    |  1 | SIMPLE      | t2    | eq_ref | c2            | c2   | 4       | opt.t1.c1 |    1 | Using index |
    +----+-------------+-------+--------+---------------+------+---------+-----------+------+-------------+
    2 rows in set (0.00 sec)
    
    
     

    0.0  ref

    ref相比叫eq_ref少了eq,即不需要等值的结果只有一行,但是还是需要reference,可以和const直接进行比较,也可以根据reference table中的列进行比较,即可以根据索引进行匹配,但匹配结果不许要唯一。

    mysql> create table t1(c1 int, c2 int);
    Query OK, 0 rows affected (0.17 sec)
    
    mysql> insert into t1 values(1,1), (2,2);
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> create table t2(c1 int, c2 int, primary key(c1,c2));
    Query OK, 0 rows affected (0.29 sec)
    
    mysql> insert into t2 values(1,1), (2,2);
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0
    
    mysql> explain select * from t1,t2 where t1.c1=t2.c1;
    +----+-------------+-------+------+---------------+---------+---------+-----------+------+-------------+
    | id | select_type | table | type | possible_keys | key     | key_len | ref       | rows | Extra       |
    +----+-------------+-------+------+---------------+---------+---------+-----------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL  | NULL          | NULL    | NULL    | NULL      |    2 | Using where |
    |  1 | SIMPLE      | t2    | ref  | PRIMARY       | PRIMARY | 4       | opt.t1.c1 |    1 | Using index |
    +----+-------------+-------+------+---------------+---------+---------+-----------+------+-------------+
    2 rows in set (0.00 sec)
    
    
     

    0.0  ref_or_null

    ref_or_null和ref相似,但是多了null的判断,where条件中可以对相应的索引key is null的判断。

    mysql> create table t1(c1 int, c2 int, key(c1, c2));
    Query OK, 0 rows affected (0.42 sec)
    
    mysql> explain select * from t1 where c1=1 or c1 is null;
    +----+-------------+-------+-------------+---------------+------+---------+-------+------+--------------------------+
    | id | select_type | table | type        | possible_keys | key  | key_len | ref   | rows | Extra                    |
    +----+-------------+-------+-------------+---------------+------+---------+-------+------+--------------------------+
    |  1 | SIMPLE      | t1    | ref_or_null | c1            | c1   | 5       | const |    2 | Using where; Using index |
    +----+-------------+-------+-------------+---------------+------+---------+-------+------+--------------------------+
    1 row in set (0.00 sec)
    
    
     

    0.0  index_merge

    index_merge是针对一个表的,根据where条件,需要利用到多个index分别进行查询,然后进行merge,merge操作就包括三种: intersect,union和sort_union。

    mysql> create table t1(c1 int, c2 int, key(c1), key(c2));
    Query OK, 0 rows affected (0.21 sec)
    mysql> delimiter /
    mysql> drop procedure if exists p1;
        -> /
    Query OK, 0 rows affected, 1 warning (0.03 sec)
    mysql> create procedure p1(count int)
        -> begin
        -> set @i=1;
        -> repeat
        -> set @i=@i+1;
        -> insert into t1 values(@i,@i);
        -> until @i > count
        -> end repeat;
        -> end
        -> /
    Query OK, 0 rows affected (0.03 sec)
    
    mysql> call p1(1000);
        -> /
    Query OK, 1 row affected (1.00 sec)
    
    mysql> commit;
        -> /
    Query OK, 0 rows affected (0.36 sec)
    ql> delimiter ;
    mysql> explain select * from t1 where c1=100 or c2 = 100;
    +----+-------------+-------+-------------+---------------+-------+---------+------+------+---------------------------------+
    | id | select_type | table | type        | possible_keys | key   | key_len | ref  | rows | Extra                           |
    +----+-------------+-------+-------------+---------------+-------+---------+------+------+---------------------------------+
    |  1 | SIMPLE      | t1    | index_merge | c1,c2         | c1,c2 | 5,5     | NULL |    2 | Using union(c1,c2); Using where |
    +----+-------------+-------+-------------+---------------+-------+---------+------+------+---------------------------------+
    1 row in set (0.00 sec)
    
    

    由于计划的选择需要统计IO和CPU代价,故需要初始化一些数据,才能使得index_merge的代价相对其他的计划是代价最小的。

     

    0.0  range

    range即范围查询,当然也需要利用到索引,主索引或者二级索引。

    mysql> create table t1(c1 int primary key, c2 int);
    Query OK, 0 rows affected (0.18 sec)
    mysql> explain select * from t1 where c1 > 10;
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    | id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | range | PRIMARY       | PRIMARY | 4       | NULL |    1 | Using where |
    +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
    1 row in set (0.01 sec)
    
    
     

    0.0  index

    二级索引上的全索引扫描,比主索引的全表扫描效率稍好,因为二级索引的I/O代价相对主索引较小。

    mysql> create table t1(c1 int, c2 int, c3 varchar(100), key(c1, c2));
    Query OK, 0 rows affected (0.23 sec)
    mysql> explain select c1,c2 from t1 where c2 > 10;
    +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
    | id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                    |
    +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
    |  1 | SIMPLE      | t1    | index | NULL          | c1   | 10      | NULL |    1 | Using where; Using index |
    +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+
    1 row in set (0.00 sec)
    
    
    
     

    0.0  all

    all表示主索引全表扫描,是性能最差的,如果计划中看到有大表的ALL,尽量通过建立二级索引或者一些参数设置,改变其执行计划。

    mysql> drop table t1;
    Query OK, 0 rows affected (0.08 sec)
    mysql> create table t1(c1 int primary key, c2 int unique, c3 int)
        -> ;
    Query OK, 0 rows affected (0.25 sec)
    mysql> explain select * from t1 where c3 = 100;
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | t1    | ALL  | NULL          | NULL | NULL    | NULL |    1 | Using where |
    +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
    1 row in set (0.00 sec)
    
    
     




    File translated from TEX by TTH, version 4.03.
    On 29 Dec 2012, 11:12.

    踏着落叶,追寻着我的梦想。转载请注明出处
  • 相关阅读:
    单例模式
    grails2.3.11第二课
    grails2.3.11第一课
    【安全】requests和BeautifulSoup小试牛刀
    【解决】国内访问github过慢
    基于Ubuntu14.10的Hadoop+HBase环境搭建
    基于adt-bundle的Android开发环境搭建
    【解决】SAE部署Django1.6+MySQL
    【解决】Django项目废弃SQLite3拥抱MySQL
    【OpenGL】画立方体
  • 原文地址:https://www.cnblogs.com/nocode/p/2838561.html
Copyright © 2011-2022 走看看