带有2个连接的MySQL查询,大型keylen导致'复制到磁盘上的tmp表'进程永远挂起

时间:2013-11-19 13:14:01

标签: mysql sql codeigniter join sql-order-by

我确信我必须做一些愚蠢的事情,但通常情况下我无法弄清楚它是什么。

我正在尝试运行此查询:

SELECT `f`.`FrenchWord`, `f`.`Pronunciation`, `e`.`EnglishWord`
FROM (`FrenchWords` f)
INNER JOIN `FrenchEnglishMappings` m ON `m`.`FrenchForeignKey`=`f`.`id`
INNER JOIN `EnglishWords` e ON `e`.`id`=`m`.`EnglishForeignKey`
WHERE `f`.`Pronunciation` =  '[whatever]';

当我运行它时,发生的事情对我来说似乎很奇怪。我得到的查询结果很好,2行约0.002秒。

但是,我的CPU也出现了大幅增长,SHOW PROCESSLIST显示了该查询的两个相同进程,状态为'复制到磁盘上的tmp表'。这些似乎一直无休止地运行,直到我杀死它们或系统冻结。

所涉及的表都不大 - 每行数在100k到600k之间。 tmp_table_sizemax_heap_table_size均为16777216。

编辑:EXPLAIN在声明中给出:

+编辑将发音的keylen缩小为112

+----+-------------+-------+--------+-------------------------------------------------------------+-----------------+---------+----------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                               | key             | key_len | ref                        | rows | Extra                                        |
+----+-------------+-------+--------+-------------------------------------------------------------+-----------------+---------+----------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | f     | ref    | PRIMARY,Pronunciation                                       | Pronunciation   | 112     | const                      |    2 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | m     | ref    | tmpindex,CombinedIndex,FrenchForeignKey,EnglishForeignKey   | tmpindex        | 4       | dict.f.id                  |    1 | Using index                                  |
|  1 | SIMPLE      | e     | eq_ref | PRIMARY,id                                                  | PRIMARY         | 4       | dict.m.EnglishForeignKey   |    1 |                                              |
+----+-------------+-------+--------+-------------------------------------------------------------+-----------------+---------+----------------------------+------+----------------------------------------------+

如果有人能够指出导致这种情况的原因,我将不胜感激。 我真正不理解的是MySQL正在做什么 - 当然,如果查询完成,那么它不需要做任何其他事情吗?

更新

感谢所有回复。我从所有人那里学到了一些东西。遵循nrathaus的建议,这个查询大大加快了。我在FrenchWords中添加了一个发音哈希二进制(16)列,其中包含unhex(md5(发音))。这是使用16的keylen索引(对于发音中的varchar索引为600+),现在查询速度要快得多。

3 个答案:

答案 0 :(得分:1)

您滥用GROUP BY。除非您的MAX(something)子句中还包含COUNT(*)SELECT等摘要功能,否则此子句完全没有意义。

尝试删除GROUP BY并查看是否有帮助。

目前尚不清楚您要对GROUP BY做些什么。但是,如果您尝试对结果集进行重复数据删除,则可以尝试SELECT DISTINCT

答案 1 :(得分:1)

正如EXPLAIN所说,你的密钥大小是巨大的:602,这需要MySQL写下数据。

你需要减少(大大)keylen,我相信建议低于128。

我建议你创建一个名为MD5_FrenchWord的列,它将包含FrenchWord的MD5值。然后将此列用于GROUP BY。这假设您在分组时而不是实际值

时寻找相似之处

答案 2 :(得分:1)

进一步研究这个问题,看起来你可能会从一些复合指数中受益。

首先,您能确保您的表格声明在尽可能多的列中有NOT NULL吗?

其次,您正在从Frenchwords表中检索发音,FrenchWord和id,因此请在该表上尝试此复合索引。然后,您的查询将能够直接从索引中获取所需内容,从而节省了大量磁盘io。请注意,在复合索引声明中首先提到发音,因为这是您要搜索的值。这允许MySQL对索引进行查找,并直接从索引中获取所需的其他信息,而不会反击到表本身。

(Pronunciation, FrenchWord, id)

您正在通过ID查找英文字样的英文字样。因此,同样的推理可以适用于这个复合指数。

(id, Englishword)

最后,一旦你使用SELECT DISTINCT,我无法判断你的ORDER BY是什么。你可能会尝试摆脱它。但它可能没什么区别。

试一试。如果您的MySQL服务器在进行这些更改后仍在抖动,则会出现某种配置问题。