当加入一个非常小/空的表时,为什么MySQL会在我使用“限制”的情况下进行完全扫描?
编辑:我从示例查询中删除了GROUPBY子句,但是当我将表x连接到一个空的/1行表y时,同样的问题会出现。尽管我使用了limit,MySQL还是对表x进行了完整的表扫描 原始问题: 我试图学习如何优化SQL查询,但遇到了一个我无法理解的行为。有这样的模式当加入一个非常小/空的表时,为什么MySQL会在我使用“限制”的情况下进行完全扫描?,mysql,sql,performance,Mysql,Sql,Performance,编辑:我从示例查询中删除了GROUPBY子句,但是当我将表x连接到一个空的/1行表y时,同样的问题会出现。尽管我使用了limit,MySQL还是对表x进行了完整的表扫描 原始问题: 我试图学习如何优化SQL查询,但遇到了一个我无法理解的行为。有这样的模式 CREATE TABLE `event` ( `ev_id` int(11) NOT NULL AUTO_INCREMENT, `ev_note` varchar(255) DEFAULT NULL, PRIMARY KEY
CREATE TABLE `event` (
`ev_id` int(11) NOT NULL AUTO_INCREMENT,
`ev_note` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ev_id`)
) ENGINE=InnoDB;
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
CREATE TABLE `table2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
查询1快速
说明
+---+-----------+---------------+------+-----------------------------------+----------+---------+---------------+------+-----------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+---------------+------+-----------------------------------+----------+---------+---------------+------+-----------+
|1 |SIMPLE |users |index |PRIMARY,fk_country_idx | PRIMARY |4 | |2 | |
|1 |SIMPLE |user_school_mm |ref |PRIMARY,fk_user_school_mm_user_idx | PRIMARY |4 |tests.users.id |1 |Using index|
+---+-----------+---------------+------+-----------------------------------+----------+---------+---------------+------+-----------+
查询2慢
说明
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|1 |SIMPLE |users |ALL | PRIMARY,fk_country_idx | | | | 10 | Using temporary; Using filesort |
|1 |SIMPLE |country |ALL | PRIMARY | | | | 1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|1 |SIMPLE |event |index | |PRIMARY |4 | | 2 | |
|1 |SIMPLE |table2 |eq_ref|PRIMARY |PRIMARY |4 |tests.event.ev_id | 1 | |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|1 |SIMPLE |event |ALL | | | | |33506704 | Using temporary; Using filesort |
|1 |SIMPLE |table1 |ALL |PRIMARY | | | |1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
我不明白幕后发生了什么,我想如果我使用users表的主键进行排序和分组,MySQL将获取users表的前2行并继续连接,但它似乎没有这样做,并在查询2中扫描了整个表
为什么MySQL扫描query2中的整个表,而只扫描query1中的前两行
MySQL版本是5.6.38经过一些测试,如果第二个tableuser\u school\u mm有一些数据,MySQL不会对第一个表进行全表扫描,如果第二个tablecountry没有数据/数据很少,MySQL会对1或2条记录进行全表扫描。为什么会发生这种情况?我不知道 如何繁殖 1-创建这样的模式
CREATE TABLE `event` (
`ev_id` int(11) NOT NULL AUTO_INCREMENT,
`ev_note` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ev_id`)
) ENGINE=InnoDB;
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
CREATE TABLE `table2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
2-在主表事件中插入一些我用35601000行填充的数据
3-将表1留空,并在表2中插入15行
insert into table2 (name) values
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar'),
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar'),
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar');
4-现在将主表与表2连接起来,并用表1重新测试相同的查询
查询1快速
说明
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|1 |SIMPLE |users |ALL | PRIMARY,fk_country_idx | | | | 10 | Using temporary; Using filesort |
|1 |SIMPLE |country |ALL | PRIMARY | | | | 1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|1 |SIMPLE |event |index | |PRIMARY |4 | | 2 | |
|1 |SIMPLE |table2 |eq_ref|PRIMARY |PRIMARY |4 |tests.event.ev_id | 1 | |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|1 |SIMPLE |event |ALL | | | | |33506704 | Using temporary; Using filesort |
|1 |SIMPLE |table1 |ALL |PRIMARY | | | |1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
查询2慢
说明
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|1 |SIMPLE |users |ALL | PRIMARY,fk_country_idx | | | | 10 | Using temporary; Using filesort |
|1 |SIMPLE |country |ALL | PRIMARY | | | | 1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|1 |SIMPLE |event |index | |PRIMARY |4 | | 2 | |
|1 |SIMPLE |table2 |eq_ref|PRIMARY |PRIMARY |4 |tests.event.ev_id | 1 | |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|1 |SIMPLE |event |ALL | | | | |33506704 | Using temporary; Using filesort |
|1 |SIMPLE |table1 |ALL |PRIMARY | | | |1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
MySQL版本是5.6.38经过一些测试,如果第二个tableuser\u school\u mm有一些数据,MySQL不会对第一个表进行全表扫描,如果第二个tablecountry没有数据/数据很少,MySQL会对1或2条记录进行全表扫描。为什么会发生这种情况?我不知道 如何繁殖 1-创建这样的模式
CREATE TABLE `event` (
`ev_id` int(11) NOT NULL AUTO_INCREMENT,
`ev_note` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ev_id`)
) ENGINE=InnoDB;
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
CREATE TABLE `table2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
2-在主表事件中插入一些我用35601000行填充的数据
3-将表1留空,并在表2中插入15行
insert into table2 (name) values
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar'),
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar'),
('fooBar'),('fooBar'),('fooBar'),('fooBar'),('fooBar');
4-现在将主表与表2连接起来,并用表1重新测试相同的查询
查询1快速
说明
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|1 |SIMPLE |users |ALL | PRIMARY,fk_country_idx | | | | 10 | Using temporary; Using filesort |
|1 |SIMPLE |country |ALL | PRIMARY | | | | 1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|1 |SIMPLE |event |index | |PRIMARY |4 | | 2 | |
|1 |SIMPLE |table2 |eq_ref|PRIMARY |PRIMARY |4 |tests.event.ev_id | 1 | |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|1 |SIMPLE |event |ALL | | | | |33506704 | Using temporary; Using filesort |
|1 |SIMPLE |table1 |ALL |PRIMARY | | | |1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
查询2慢
说明
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
|1 |SIMPLE |users |ALL | PRIMARY,fk_country_idx | | | | 10 | Using temporary; Using filesort |
|1 |SIMPLE |country |ALL | PRIMARY | | | | 1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+------------------------+-----+---------+-----+------+---------------------------------------------------+
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
|1 |SIMPLE |event |index | |PRIMARY |4 | | 2 | |
|1 |SIMPLE |table2 |eq_ref|PRIMARY |PRIMARY |4 |tests.event.ev_id | 1 | |
+---+-----------+--------+------+----------------+--------+---------+------------------+------+--------+
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|id |select_type|table | type | possible_keys | key | key_len | ref | rows | Extra |
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
|1 |SIMPLE |event |ALL | | | | |33506704 | Using temporary; Using filesort |
|1 |SIMPLE |table1 |ALL |PRIMARY | | | |1 | Using where; Using join buffer (Block Nested Loop)|
+---+-----------+--------+------+----------------+--------+---------+-------+---------+---------------------------------------------------+
MySQL版本是5.6.38主要原因是误用了GROUP BY。让我们来看第一个问题。尽管速度很快,但仍然是错误的:
SELECT *
FROM users
LEFT JOIN user_school_mm on users.id = user_school_mm.user_id
GROUP BY users.id
ORDER BY users.id ASC
LIMIT 2
一个用户可以上两所学校。使用多:多映射的用户_school_mm声称这是一种可能性。因此,在完成联接之后,您可以为单个用户获得两行。但是,您可以按users.id分组,将其归结为一行。但是您应该使用两个学校id值中的哪一个
在您提出有意义的查询之前,我不会尝试解决性能问题。在这一点上,可以更容易地指出为什么一个查询比另一个查询性能更好。主要原因是GROUP BY的误用。让我们来看第一个问题。尽管速度很快,但仍然是错误的:
SELECT *
FROM users
LEFT JOIN user_school_mm on users.id = user_school_mm.user_id
GROUP BY users.id
ORDER BY users.id ASC
LIMIT 2
一个用户可以上两所学校。使用多:多映射的用户_school_mm声称这是一种可能性。因此,在完成联接之后,您可以为单个用户获得两行。但是,您可以按users.id分组,将其归结为一行。但是您应该使用两个学校id值中的哪一个
在您提出有意义的查询之前,我不会尝试解决性能问题。在这一点上,可以更容易地指出为什么一个查询比另一个查询性能更好。MySQL优化器将首先决定连接顺序/方法,然后检查对于所选的连接顺序,是否可以使用索引避免排序。对于这个问题中的慢速查询,优化器决定使用块嵌套循环BNL join 当其中一个表非常小且没有限制时,BNL通常比使用索引更快 但是,使用BNL,行不一定按照第一个表给出的顺序出现。因此,在应用限制之前,需要对联接结果进行排序
您可以通过设置优化器_开关='block_nested_loop=off'来关闭BNL MySQL优化器将首先决定连接顺序/方法,然后检查对于所选的连接顺序,是否可以使用索引避免排序。对于这个问题中的慢速查询,优化器决定使用块嵌套循环BNL join 当其中一个表非常小且没有限制时,BNL通常比使用索引更快 但是,使用BNL,行不一定按照第一个表给出的顺序出现。因此,在应用限制之前,需要对联接结果进行排序
您可以通过设置优化器_开关='block_nested_loop=off'来关闭BNL 我猜一旦找到足够的行,它就会结束扫描。@shawnt00但在查询2中,它扫描了整个表,在这个示例中有10行,如果用户选项卡上有300万行
le是300万行你的好查询也不好。。。选择*。。。。GROUP BY users.id是错误的ansi GROUP BY SQL。使用select*和group by,可能会导致错误的不相关数据—这些是格式错误的查询。也许这解释了为什么MySQL不尝试优化它们。这些都很容易被更合适的逻辑所取代,从而产生正确的执行计划。很高兴你把它整理好了。实际上,我只是第一次浏览了一下,错过了一个重要的细节。我猜一旦找到足够多的行,扫描就会结束。@shawnt00但在查询2中,它扫描了整个表,在这个示例中有10行,如果用户表是300万行,则扫描了300万行。好的查询也不好。。。选择*。。。。GROUP BY users.id是错误的ansi GROUP BY SQL。使用select*和group by,可能会导致错误的不相关数据—这些是格式错误的查询。也许这解释了为什么MySQL不尝试优化它们。这些都很容易被更合适的逻辑所取代,从而产生正确的执行计划。很高兴你把它整理好了。事实上,我只是第一次浏览了一下,错过了一个重要的细节。非常感谢您的关注,我删除了群组,MySQL仍然会做出同样的行为。如果我将表连接到一个空表,MySQL将对第一个表进行完全扫描。我在答案中提供了如何复制的详细步骤。非常感谢您的关注,我删除了Groupby,MySQL仍然会做出相同的行为。如果我将表连接到一个空表,MySQL将对第一个表进行完全扫描。我在回答如何复制时提供了详细的步骤。你……是……国王。从现在起,我会读你说的每一个字。非常感谢您的回答,也感谢您的MySQL。您……是……国王。从现在起,我会读你说的每一个字。非常感谢您的回答,也感谢您的MySQL。