Mysql Join大表小表到底谁驱动谁

1、准备

mysql> create table dept(
    id int unsigned auto_increment not null primary key,
    name varchar(20) default ‘‘ not null,
    key(name)
)engine=innodb default charset=utf8mb4;
CREATE TABLE `userinfo` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,
  `passwd` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `phone` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `dept` int NOT NULL DEFAULT ‘0‘,
  PRIMARY KEY (`id`),
  KEY `name` (`name`),
  KEY `dept` (`dept`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

用 excel 伪造数据,并导入

mysql> load data infile ‘/var/lib/mysql-files/new5.txt‘ into table dept fields terminated by ‘ ‘ lines terminated by ‘\n‘;
Query OK, 1000 rows affected (0.03 sec)
Records: 1000  Deleted: 0  Skipped: 0  Warnings: 0

mysql> load data infile ‘/var/lib/mysql-files/new.txt‘ into table userinfo fields terminated by ‘ ‘ lines terminated by ‘\n‘;
Query OK, 120000 rows affected (0.02 sec)
Records: 120000  Deleted: 0  Skipped: 0  Warnings: 0

dept 1000 条, userinfo 120000 条

一、查找 userinfo 名字是 a25 的 id,name,及 dept name

由于有 userinfo = ‘a25‘ 的限制条件,不管谁 join 谁,或者 left join,结果都是一样的

mysql> select count(1) from userinfo left join dept using(dept_id) where userinfo.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      187 |
+----------+
1 row in set

mysql> select count(1) from dept left join userinfo using(dept_id) where userinfo.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      187 |
+----------+
1 row in set

mysql> select count(1) from dept join userinfo using(dept_id) where userinfo.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      187 |
+----------+
1 row in set

mysql> select count(1) from userinfo join dept using(dept_id) where userinfo.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      187 |
+----------+
1 row in set

那就比较四种方式的效率:

开启慢查询:

set long_query_time = 0.0001;
set global slow_query_log = ON;
mysql> show variables like "slow%";
+---------------------+--------------------------------------+
| Variable_name       | Value                                |
+---------------------+--------------------------------------+
| slow_launch_time    | 2                                    |
| slow_query_log      | ON                                   |
| slow_query_log_file | /var/lib/mysql/muqing-web-2-slow.log |
+---------------------+--------------------------------------+
tail -f /var/lib/mysql/muqing-web-2-slow.log // 实时查看耗时

1、userinfo left join dept

mysql> explain select userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ref    | name,name_2   | name    | 1022    | const                 |  187 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.userinfo.dept_id |    1 |      100 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+------------+
| Variable_name   | Value      |
+-----------------+------------+
| Last_query_cost | 130.899000 |
+-----------------+------------+
1 row in set

mysql> explain select straight_join userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ref    | name,name_2   | name    | 1022    | const                 |  187 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.userinfo.dept_id |    1 |      100 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+------------+
| Variable_name   | Value      |
+-----------------+------------+
| Last_query_cost | 130.899000 |
+-----------------+------------+
1 row in set

可以看到优化器选择的是顺序是先 userinfo 表,warnings 信息:

mysql> explain select straight_join userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ref    | name,name_2   | name    | 1022    | const                 |  187 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.userinfo.dept_id |    1 |      100 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
2 rows in set

mysql> show warnings;
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                                            |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select straight_join `test`.`userinfo`.`user_id` AS `user_id`,`test`.`userinfo`.`name` AS `name`,`test`.`dept`.`name` AS `name` from `test`.`userinfo` left join `test`.`dept` on((`test`.`userinfo`.`dept_id` = `test`.`dept`.`dept_id`)) where (`test`.`userinfo`.`name` = ‘a25‘) |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set

2、userinfo join dept

mysql> explain select userinfo.user_id, userinfo.name, dept.name from userinfo join dept using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ref    | name,name_2   | name    | 1022    | const                 |  187 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.userinfo.dept_id |    1 |      100 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+------------+
| Variable_name   | Value      |
+-----------------+------------+
| Last_query_cost | 130.899000 |
+-----------------+------------+
1 row in set

mysql> explain select straight_join userinfo.user_id, userinfo.name, dept.name from userinfo join dept using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ref    | name,name_2   | name    | 1022    | const                 |  187 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.userinfo.dept_id |    1 |      100 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+------+----------+-------------+
2 rows in set

mysql> show warnings;
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                                                                                         |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1003 | /* select#1 */ select straight_join `test`.`userinfo`.`user_id` AS `user_id`,`test`.`userinfo`.`name` AS `name`,`test`.`dept`.`name` AS `name` from `test`.`userinfo` join `test`.`dept` where ((`test`.`userinfo`.`name` = ‘a25‘) and (`test`.`userinfo`.`dept_id` = `test`.`dept`.`dept_id`)) |
+-------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set
# Time: 2020-04-10T17:00:38.452393Z
# User@Host: root[root] @ localhost []  Id:    11
# Query_time: 0.000741  Lock_time: 0.000137 Rows_sent: 187  Rows_examined: 374
SET timestamp=1586538038;
select sql_no_cache userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where userinfo.name = ‘a25‘;
# Time: 2020-04-10T17:01:49.946904Z
# User@Host: root[root] @ localhost []  Id:    11
# Query_time: 0.000766  Lock_time: 0.000149 Rows_sent: 187  Rows_examined: 374
SET timestamp=1586538109;
select straight_join userinfo.user_id, userinfo.name, dept.name from userinfo join dept using(dept_id) where userinfo.name = ‘a25‘;

可以看出两个时间差不多

3、dept left join userinfo

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept left join userinfo using(dept_id) where userinfo.name = ‘a25‘;

+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | dept     | NULL       | index | PRIMARY       | name | 82      | NULL  | 1000 |      100 | Using index |
|  1 | SIMPLE      | userinfo | NULL       | ref   | name,name_2   | name | 1022    | const |  187 |       10 | Using where |
+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+---------------+
| Variable_name   | Value         |
+-----------------+---------------+
| Last_query_cost | 106671.417848 |
+-----------------+---------------+
1 row in set
# Time: 2020-04-10T17:32:02.890446Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.134147  Lock_time: 0.000128 Rows_sent: 187  Rows_examined: 188000
SET timestamp=1586539922;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept left join userinfo using(dept_id) where userinfo.name = ‘a25‘;

明显比上面的要慢

4、dept join userinfo

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept join userinfo using(dept_id) where userinfo.name = ‘a25‘;
+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key  | key_len | ref   | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | dept     | NULL       | index | PRIMARY       | name | 82      | NULL  | 1000 |      100 | Using index |
|  1 | SIMPLE      | userinfo | NULL       | ref   | name,name_2   | name | 1022    | const |  187 |       10 | Using where |
+----+-------------+----------+------------+-------+---------------+------+---------+-------+------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+---------------+
| Variable_name   | Value         |
+-----------------+---------------+
| Last_query_cost | 106671.417848 |
+-----------------+---------------+
1 row in set
# Time: 2020-04-10T17:33:35.458204Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.133475  Lock_time: 0.000164 Rows_sent: 187  Rows_examined: 188000
SET timestamp=1586540015;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept join userinfo using(dept_id) where userinfo.name = ‘a25‘;

速度和 3 一样,也远大于前2 

所以是大表驱动小表。而且 show status like ‘last_query_cost‘ 还挺准的

二、查找 dept 名字是 a25 的 id,name,及 dept name

 四种查询等效

mysql> select count(1) from userinfo left join dept using(dept_id) where dept.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      360 |
+----------+
1 row in set

mysql> select count(1) from userinfo join dept using(dept_id) where dept.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      360 |
+----------+
1 row in set

mysql> select count(1) from dept join userinfo using(dept_id) where dept.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      360 |
+----------+
1 row in set

mysql> select count(1) from dept left join userinfo using(dept_id) where dept.name = ‘a25‘;
+----------+
| count(1) |
+----------+
|      360 |
+----------+
1 row in set

1、userinfo left join dept

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where dept.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows   | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ALL    | NULL          | NULL    | NULL    | NULL                  | 120234 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY,name  | PRIMARY | 4       | test.userinfo.dept_id |      1 |        5 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+--------------+
| Variable_name   | Value        |
+-----------------+--------------+
| Last_query_cost | 54209.549000 |
+-----------------+--------------+
1 row in set
# Time: 2020-04-10T17:40:53.462276Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.110679  Lock_time: 0.000145 Rows_sent: 360  Rows_examined: 240000
SET timestamp=1586540453;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from userinfo left join dept using(dept_id) where dept.name = ‘a25‘;

2、userinfo join dept

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from userinfo join dept using(dept_id) where dept.name = ‘a25‘;
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
| id | select_type | table    | partitions | type   | possible_keys | key     | key_len | ref                   | rows   | filtered | Extra       |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
|  1 | SIMPLE      | userinfo | NULL       | ALL    | NULL          | NULL    | NULL    | NULL                  | 120234 |      100 | NULL        |
|  1 | SIMPLE      | dept     | NULL       | eq_ref | PRIMARY,name  | PRIMARY | 4       | test.userinfo.dept_id |      1 |        5 | Using where |
+----+-------------+----------+------------+--------+---------------+---------+---------+-----------------------+--------+----------+-------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+--------------+
| Variable_name   | Value        |
+-----------------+--------------+
| Last_query_cost | 54209.549000 |
+-----------------+--------------+
# Time: 2020-04-10T17:44:21.701356Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.105262  Lock_time: 0.000137 Rows_sent: 360  Rows_examined: 240000
SET timestamp=1586540661;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from userinfo join dept using(dept_id) where dept.name = ‘a25‘;

速度比上面的好一点点

3、dept left join userinfo

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept left join userinfo using(dept_id) where dept.name = ‘a25‘;
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows   | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | dept     | NULL       | ref  | name          | name | 82      | const |      1 |      100 | Using index                                        |
|  1 | SIMPLE      | userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL  | 120234 |      100 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+--------------+
| Variable_name   | Value        |
+-----------------+--------------+
| Last_query_cost | 12128.032803 |
+-----------------+--------------+
# Time: 2020-04-10T17:47:06.147081Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.031848  Lock_time: 0.000137 Rows_sent: 360  Rows_examined: 120001
SET timestamp=1586540826;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept left join userinfo using(dept_id) where dept.name = ‘a25‘;

速度竟然比上面的块,不是大表驱动小表吗,继续看

4、dept join userinfo

mysql> explain select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept join userinfo using(dept_id) where dept.name = ‘a25‘;
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref   | rows   | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | dept     | NULL       | ref  | PRIMARY,name  | name | 82      | const |      1 |      100 | Using index                                        |
|  1 | SIMPLE      | userinfo | NULL       | ALL  | NULL          | NULL | NULL    | NULL  | 120234 |       10 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+-------+--------+----------+----------------------------------------------------+
2 rows in set

mysql> show status like ‘last_query_cost‘;
+-----------------+--------------+
| Variable_name   | Value        |
+-----------------+--------------+
| Last_query_cost | 12128.032803 |
+-----------------+--------------+
# Time: 2020-04-10T17:48:44.738339Z
# User@Host: root[root] @  [10.0.0.105]  Id:    13
# Query_time: 0.032005  Lock_time: 0.000143 Rows_sent: 360  Rows_examined: 120001
SET timestamp=1586540924;
select sql_no_cache straight_join userinfo.user_id, userinfo.name, dept.name from dept join userinfo using(dept_id) where dept.name = ‘a25‘;

速度也比 1,2快

left join 和 join 相比 按理说会慢一点,因为:join 的伪代码为

outer_iter = iterator over tb1 where col1 in(5, 6)

outer_row = outer_iter.next

while outer_row

  inner_iter = iterator over tb2 where col3 = outer_row.col3

  inner_row = inner_iter.next

  while inner_row

    out_put [ outer_row.col1, inner_row.col2 ]
    inner_row = inner_iter.next
  end

  outer_row = outer_iter.next

end

而 left join 会多一次判断,多一次返回 null 结果集。

猜想是 where 条件 获取的过滤多,就用谁来做外表,所以讲不定 一定是大表驱动小表

相关推荐