MySQL join的自动优化

MySQL的多表Join查询是会自动优化顺序的,本文将以一个实例讲述。

1、首先,编写一个建表脚本

[roo@localhost test]$ vi createA.sql
use test;
DROP PROCEDURE IF EXISTS PcreateA;
drop table if exists A;
drop table if exists B;
drop table if exists C;
create table A(
     aid int(11) primary key,
     title varchar(16),
     content varchar(16),
     uid int(11),
     tid int(11)
);
create table B(
     uid int(11) primary key,
     uname varchar(16)
);
create table C(
    tid int(11) primary key,
    tname varchar(16),
    age int(11)
);
DELIMITER //
CREATE procedure PcreateA() # 创建无参存储过程
BEGIN
  DECLARE i INT default 0;
  WHILE i<40000 DO # 结束循环的条件: 当i大于n时跳出while循环
      INSERT INTO A VALUES(i,concat("title",i),'content', i%4000, i%100);  # 往表添数据
      SET i = i+1;    # 循环一次,i加1
  END WHILE;  # 结束while循环
  SELECT count(*) FROM A; # 查看表数据

  set i=0;
  WHILE i<8000 DO # 结束循环的条件: 当i大于n时跳出while循环
      INSERT INTO B VALUES(i,concat('uname',i));  # 往表添数据
      SET i = i+1;    # 循环一次,i加1
  END WHILE;  # 结束while循环
  SELECT count(*) FROM B; # 查看表数据

  set i=0;
  WHILE i<200 DO # 结束循环的条件: 当i大于n时跳出while循环
      INSERT INTO C VALUES(i,concat('tname',i),i%10);  # 往表添数据
      SET i = i+1;    # 循环一次,i加1
  END WHILE;  # 结束while循环
  SELECT count(*) FROM C; # 查看表数据
END;
//  # 结束定义语句
DELIMITER ; # 重新将分隔符设置为;
CALL PcreateA();    # 调用存储过程
 

2、编写查询脚本

[roo@localhost test]$ vi selectjoin.sql
use test;
explain select count(*) from (SELECT A.aid, A.title, B.uname, C.tname, C.age FROM A LEFT JOIN B ON A.uid=B.uid left JOIN C ON A.tid=C.tid where C.age<1) AA;
explain select count(*) from (select AC.aid, AC.title from (SELECT A.aid, A.title, A.uid, C.tname, C.age FROM A LEFT JOIN C ON A.tid=C.tid where C.age<1) AC LEFT JOIN B ON AC.uid=B.uid) BB;
 

3、依次执行脚本看结果

mysql> source /home/roo/test/createA.sql

(1)去掉selectjoin.sql中的explain,执行

Database changed
+----------+
| count(*) |
+----------+
|     4000 |
+----------+
1 row in set (0.08 sec)

+----------+
| count(*) |
+----------+
|     4000 |
+----------+
1 row in set (0.08 sec)
发现两者居然时间相同,难道不应该第二个“提前剪枝”的更优么??。。。。。

(2)执行explain查看执行计划

mysql> source /home/roo/test/selectjoin.sql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref        | rows  | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
|  1 | SIMPLE      | A     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL       | 39303 |   100.00 | NULL        |
|  1 | SIMPLE      | C     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.A.tid |     1 |    33.33 | Using where |
|  1 | SIMPLE      | B     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.A.uid |     1 |   100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)

+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref        | rows  | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
|  1 | SIMPLE      | A     | NULL       | ALL    | NULL          | NULL    | NULL    | NULL       | 39303 |   100.00 | NULL        |
|  1 | SIMPLE      | C     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.A.tid |     1 |    33.33 | Using where |
|  1 | SIMPLE      | B     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.A.uid |     1 |   100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+---------+---------+------------+-------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
 

发现两者执行计划相同。

4、结果分析

MySQL执行语句过程中涉及到两大流程:优化器和执行器。其中优化器最主要的任务,是选择索引和在多表连接时选择连接顺序。

join顺序的选择会影响执行性能,确定join执行顺序就需要估算所有join操作的代价。

默认配置下MySQL会估算所有可能的组合,由于MySQL里限制一个查询的join表数目上限为61,所以MySQL有一个参数叫做optimizer_search_depth,默认为62,限制优化器的深度。

加入有20张表join查询,如optimizer_search_depth为4,则评估次数为20*19*18*17。

mysql> show variables like '%optimizer_search_depth%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| optimizer_search_depth | 62    |
+------------------------+-------+
在本例中,MySQL优化器执行了优化,所以两者都选取了最优的顺序。

 

 

上一篇:Bootstrap


下一篇:css类名规范BEM用法