带有多个选择语句的 MySQL 右外连接非常慢
Posted
技术标签:
【中文标题】带有多个选择语句的 MySQL 右外连接非常慢【英文标题】:MySQL right outer join with multiple select statements is very slow 【发布时间】:2019-12-28 07:41:46 【问题描述】:我有 3 个表格,如下所示:
解析表(table1)
+----+---------+-----+------------+--------+
| id | created | num | resolution | score |
+----+---------+-----+------------+--------+
作者表(table2)
+----+---------+--------+------+------+--------+
| id | created | author | file | tags | res_id |
+----+---------+--------+------+------+--------+
状态表(table3)
+----+------+-------------+----------+--------+-----------+
| id | name | description | category | status | author_id |
+----+------+-------------+----------+--------+-----------+
我正在使用 2 个 select 语句根据它们的 ID 从同一个表中获取记录:
statement1(分辨率数 = 1357)
select *
from table1, table2, table3
where table1.num=1357
and table1.id=table2.res_id
and table2.id=table3.author_id;
statement2(分辨率数 = 1358)
select *
from table1, table2, table3
where table1.num=1358
and table1.id=table2.res_id
and table2.id=table3.author_id;
注意:每个语句返回 100,000+ 条记录
我使用右外连接来获取 statement2 中不存在于 statement1 结果中的记录,如下所示:
select * from (
select *
from table1, table2, table3
where table1.num=1357
and table1.id=table2.res_id
and table2.id=table3.author_id
) as tab1
right outer join (
select *
from table1, table2, table3
where table1.num=1358
and table1.id=table2.res_id
and table2.id=table3.author_id
) as tab2 on tab1.name=tab2.name
and tab1.category=tab2.category
and tab1.author=tab2.author
where tab1.name is NULL
and tab1.category is NULL
and tab1.author is NULL
这适用于少量记录,但在我的情况下,总共有 200,000 条记录,通常需要 11 分钟才能返回所需的结果。
如何优化查询以更快地获取结果?
PS:
对table1.num、table2.res_id、table3.author_id应用索引后
显示创建表table1;
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`num` int(11) NOT NULL,
`resolution` varchar(100) NOT NULL,
`score` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `num` (`num`)
KEY `num_index` (`num`)
) ENGINE=InnoDB AUTO_INCREMENT=2552 DEFAULT CHARSET=latin1
显示创建表table2;
CREATE TABLE `table2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created` datetime NOT NULL,
`author` varchar(200) NOT NULL,
`file` varchar(200) NOT NULL,
`tags` varchar(200) DEFAULT NULL,
`res_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `table2_res_id_48009073_fk_table1_id` (`res_id`),
KEY `res_id_index` (`res_id`),
CONSTRAINT `table2_res_id_48009073_fk_table1_id ` FOREIGN KEY (`res_id`) REFERENCES `table1` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=686972 DEFAULT CHARSET=latin1
显示创建表table3;
CREATE TABLE `table3` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`description` longtext NOT NULL,
`category` varchar(200) NOT NULL,
`status` varchar(20) NOT NULL,
`author_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `table3_ffe73c23` (`author_id`),
KEY `author_id_index` (`author_id`)
CONSTRAINT `table3_id_e47d088c_fk_table2_id` FOREIGN KEY (`author_id`) REFERENCES `table2` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12042452 DEFAULT CHARSET=latin1
解释
+------+----------------+-----------+---------+--------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------+--------------+-------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+----------------+-----------+---------+--------------------------------------------------------------------------------------+-------------------------------------------------------------+--------------+--------------+-------+------------------------------------+
| 1 | SIMPLE | table1 | const | PRIMARY,num,num_index | num | 4 | const | 1 | Using index |
| 1 | SIMPLE | table2 | ref | PRIMARY,table2_res_id_48009073_fk_table1_id,res_id_index. | table2_res_id_48009073_fk_table1_id. | 4 | const | 7106 | |
| 1. | SIMPLE | table3 | ref | table3_ffe73c23,author_id_index | table3_ffe73c23 | 4 | t2.id | 9 | |
| 1 | SIMPLE | table1 | const | PRIMARY,num,num_index | num | 4 | const | 1 | Using where; Using index |
| 1 | SIMPLE | table2 | ref | PRIMARY, table2_res_id_48009073_fk_table1_id,res_id_index | table2_res_id_48009073_fk_table1_id. | 4 | t1.id. | 213 | Using where |
| 1 | SIMPLE | table3 | ref | table3_ffe73c23, author_id_index | table3_ffe73c23 | 4 | t2.id | 9 | Using where; Not exists |
+------+----------------+----------+----------+-------------------------------------------------------------------------------------+-------------------------------------------------------------+---------------+---------------+-------+-----------------------------------+
【问题讨论】:
尝试将子查询的结果放在临时表中,以便为它们添加索引。 请提供三个表的SHOW CREATE TABLE tablename
语句。所提供的信息将为回答您的问题提供很大帮助。
@VinayPotluri 谢谢。我现在正在调查你的问题。请注意,RickJames 建议的索引已经存在,因此您无需再次添加它们。我会放弃这些,这样它们就不会减慢您插入这些表的速度。
什么版本的 mysql?
@VinayPotluri - 请提供EXPLAIN SELECT ...
【参考方案1】:
where table1.num=1357
and table1.id=table2.res_id
and table2.id=table3.author_id;
请求这些索引:
table1: INDEX(num)
table2: INDEX(res_id)
table3: INDEX(author_id)
进行这些更改,提供SHOW CREATE TABLE
,然后我们可以更好地查看问题的其余部分。
description
通常有多大?如果它通常大于几 KB,那么搬运它(table3.*
、tab3.*
)的成本非常高。如果您不需要结果中的列,则拼写*
以便排除它。
如果您确实在结果中需要description
,仍然执行上述操作,但随后将另一个JOIN
添加回table3
以获取它以及任何其他不需要的列连接。
【讨论】:
请注意,tab1
和 tab2
是派生表,因此您不能为它们创建索引。 (存在派生表这一事实可能是性能问题的主要原因,但如果它们提供 SHOW CREATE TABLE
语句,我将在我自己的答案中对此进行讨论。)
@WillemRenzema - 哎呀。我删除了它们。
@VinayPotluri - 看起来你已经有了这些索引。好吧,回到绘图板。【参考方案2】:
尝试以下重写的查询:
SELECT
table1.*,
table2.*,
table3.*
FROM table1
INNER JOIN table2
ON table2.res_id = table1.id
INNER JOIN table3
ON table3.author_id = table2.id
LEFT OUTER JOIN (
SELECT
table3.name,
table3.category,
table2.author
FROM table1
INNER JOIN table2
ON table2.res_id = table1.id
INNER JOIN table3
ON table3.author_id = table2.id
WHERE table1.num = 1357
) tab1
ON tab1.name = table3.name
AND tab1.category = table3.category
AND tab1.author = table2.author
WHERE table1.num = 1358
AND tab1.name IS NULL
单独的查询可能会显示出一些改进。如果没有太大改善,请尝试以下新索引:
table2: INDEX(res_id, author)
table3: INDEX(author_id, category, name)
然后报告结果。
注意:尝试查询时,请确保运行两次并丢弃第一个结果,以便填充 InnoDB 缓冲区缓存。
【讨论】:
我尝试了几次运行您的解决方案,但性能似乎没有提高。没有索引 - 集合中的 1631 行(53 分钟 11.668 秒) - 有索引 - 集合中的 1631 行(16 分钟 50.934 秒)。在我的情况下,连接并没有比 where 子句快以上是关于带有多个选择语句的 MySQL 右外连接非常慢的主要内容,如果未能解决你的问题,请参考以下文章
MySql的回顾五:多表查询下(内联/左外/右外/自连接/交叉)-1999语法