我有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 |
+----+-------------+-------------+-------+---------------+---------+---------+------+---------+--------------------------------+
答案 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
用于JOINing
。 INDEX(col1, id)
和INDEX(id, col1)
同样适合进入第二个表格。
我一直在说“你的简化例子”,因为只要你改变了什么,这些答案中的大多数建议都可以用来获取。
(撤回)当你有一个具有“低基数”的列,例如col%
,只有0,1,NULL可能性时,INDEX(col1)
基本上没用,因为盲目扫描更快表而不是使用索引。
另一方面,INDEX(col1, ...)
可能很有用,如第二个表所述。
然而,对于第一个表都没有用。如果你有这样的INDEX
,它将被忽略。
然后是“覆盖”。同样,您的示例是不切实际的简单化,因为除id
和col1
之外基本上没有任何字段被触及。 “覆盖”索引包括所有在查询中触及的表的字段。覆盖索引实际上总是小于数据,因此运行覆盖索引所需的工作量更少,因此更快。
(撤回撤回)INDEX(col1, id)
, 顺序是第一个表的有用覆盖索引。
想象一下,如果你没有提到col1只有3个值,我的讨论会如何。完全不同。
我们还没有ORDER BY
,IN(...)
,BETWEEN...AND...
,引擎差异,PRIMARY KEY
,LEFT JOIN
等技巧
More insight into building indexes from Selects
ANALYZE TABLE
不是必需的。