什么是连接几个匹配MySQL中特定列值的表的最快方法

时间:2015-06-06 04:36:43

标签: mysql performance

我有3个表格如下:

CREATE TABLE big_table_1 (
 id   INT(11),
 col1 TINYINT(1),
 col2 TINYINT(1),
 col3 TINYINT(1),
 PRIMARY KEY (`id`)
)

对于big_table_2和big_table_3等等。 col1,col2,col3值为0,1或null。

我正在寻找每个表中col1值等于1的id。我使用我能想到的最简单的方法加入它们如下:

SELECT t1.id 
FROM big_table_1 AS t1
INNER JOIN big_table_2 AS t2 ON t2.id = t1.id
INNER JOIN big_table_3 AS t3 ON t3.id = t1.id
WHERE t1.col1 = 1
AND t2.col1 = 1
AND t3.col1 = 1;

每个表有1000万行,查询在我的机器上执行大约需要40秒:

407231 rows in set (37.19 sec)

解释结果:

+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref          | rows     | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+
|  1 | SIMPLE      | t3    | ALL    | PRIMARY       | NULL    | NULL    | NULL         | 10999387 | Using where |
|  1 | SIMPLE      | t1    | eq_ref | PRIMARY       | PRIMARY | 4       | testDB.t3.id |        1 | Using where |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY       | PRIMARY | 4       | testDB.t3.id |        1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+--------------+----------+-------------+

如果我在col1上声明索引,结果会稍慢:

407231 rows in set (40.84 sec)

我也尝试了以下查询:

SELECT t1.id
FROM (SELECT distinct ta1.id FROM big_table_1 ta1 WHERE ta1.col1=1) as t1
WHERE EXISTS (SELECT ta2.id FROM big_table_2 ta2 WHERE ta2.col1=1 AND ta2.id = t1.id)
AND EXISTS (SELECT ta3.id FROM big_table_3 ta3 WHERE ta3.col1=1 AND ta3.id = t1.id);

但它的速度较慢:

407231 rows in set (44.01 sec) [with index on col1]
407231 rows in set (1 min 36.52 sec) [without index on col1]

前面提到的简单方法基本上是在MySQL中最快的方法吗?是否有必要将表格分成多个服务器以便更快地获得结果?

附录:根据要求提供了安德鲁代码的EXPLAIN结果(我将表格减少到仅100万行,索引在id和col1上):

+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+
| id | select_type | table       | type  | possible_keys | key     | key_len | ref  | rows    | Extra                          |
+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+
|  1 | PRIMARY     | <derived3>  | ALL   | NULL          | NULL    | NULL    | NULL |  332814 |                                |
|  1 | PRIMARY     | <derived4>  | ALL   | NULL          | NULL    | NULL    | NULL |  333237 | Using where; Using join buffer |
|  1 | PRIMARY     | <derived2>  | ALL   | NULL          | NULL    | NULL    | NULL |  333505 | Using where; Using join buffer |
|  4 | DERIVED     | big_table_3 | index | NULL          | PRIMARY | 5       | NULL | 1000932 | Using where; Using index       |
|  3 | DERIVED     | big_table_2 | index | NULL          | PRIMARY | 5       | NULL | 1000507 | Using where; Using index       |
|  2 | DERIVED     | big_table_1 | index | NULL          | PRIMARY | 5       | NULL | 1000932 | Using where; Using index       |
+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+

3 个答案:

答案 0 :(得分:1)

对于踢,尝试使用覆盖索引(id,col1的复合) 所以1个索引使它成为主要复合词。没有其他索引。

然后运行analyze table xxx(总共3次,每个表一次)

然后将其解雇,希望mysql cbo不会密集以解决它。

第二个想法是看到没有where子句的结果。在join on子句

中转换它

答案 1 :(得分:0)

你试过这个:

SELECT t1.id 
FROM 
(SELECT id from big_table_1 where col1 = 1) AS t1
INNER JOIN (SELECT id from  big_table_2 where col1 = 1)  AS t2 ON t2.id = t1.id
INNER JOIN (SELECT id from  big_table_3 where col1 = 1)  AS t3 ON t3.id = t1.id

答案 2 :(得分:0)

INNER JOIN(与JOIN相同)让优化器选择是使用左侧的表还是右侧的表。您提供的简化SELECT可以从三个表中的任何一个开始。

优化器喜欢从带有WHERE子句的表开始。您的简化示例意味着每个表都同样好 IF INDEX开始有col1 。 (见下面的撤回。)

第二个和后续表需要不同的索引规则。在您的简化示例中,col1用于过滤,id用于JOINingINDEX(col1, id)INDEX(id, col1)同样适合进入第二个表格。

我一直在说“你的简化例子”,因为只要你改变了什么,这些答案中的大多数建议都可以用来获取。

(撤回)当你有一个具有“低基数”的列,例如col%,只有0,1,NULL可能性时,INDEX(col1)基本上没用,因为盲目扫描更快表而不是使用索引。

另一方面,INDEX(col1, ...)可能很有用,如第二个表所述。

然而,对于第一个表都没有用。如果你有这样的INDEX,它将被忽略。

然后是“覆盖”。同样,您的示例是不切实际的简单化,因为除idcol1之外基本上没有任何字段被触及。 “覆盖”索引包括所有在查询中触及的表的字段。覆盖索引实际上总是小于数据,因此运行覆盖索引所需的工作量更少,因此更快。

(撤回撤回)INDEX(col1, id) 顺序是第一个表的有用覆盖索引。

想象一下,如果你没有提到col1只有3个值,我的讨论会如何。完全不同。

我们还没有ORDER BYIN(...)BETWEEN...AND...,引擎差异,PRIMARY KEYLEFT JOIN等技巧

More insight into building indexes from Selects

ANALYZE TABLE不是必需的。