MySQL查询需要太长时间 - 索引应该是什么?

时间:2013-04-16 10:48:59

标签: mysql sql indexing

这是我的问题:

CREATE TEMPORARY TABLE temptbl (
  pibn INT UNSIGNED NOT NULL, page SMALLINT UNSIGNED NOT NULL)
  ENGINE=MEMORY;
INSERT INTO temptbl (
  SELECT pibn,page FROM mytable
  WHERE word1=429907 AND word2=0);
ALTER TABLE temptbl ADD INDEX (pibn,page);
SELECT word1,COUNT(*) AS aaa
  FROM mytable a
  INNER JOIN temptbl b
  ON a.pibn=b.pibn AND a.page=b.page
  WHERE word2=0
  GROUP BY word1 ORDER BY aaa DESC LIMIT 10;
DROP TABLE temptbl;

问题是SELECT word1,COUNT(*) AS aaa,特别是计数。那个select语句需要16秒。

EXPLAIN说:

+----+-------------+-------+------+---------------------------------+-------------+---------+-------------------------------------------------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys                   | key         | key_len | ref                                                         | rows  | Extra                           |
+----+-------------+-------+------+---------------------------------+-------------+---------+-------------------------------------------------------------+-------+---------------------------------+
|  1 | SIMPLE      | b     | ALL  | pibn                            | NULL        | NULL    | NULL                                                        | 26778 | Using temporary; Using filesort |
|  1 | SIMPLE      | a     | ref  | w2pibnpage1,word21pibn,pibnpage | w2pibnpage1 | 9       | const,db.b.pibn,db.b.page                                   |     4 | Using index                     |
+----+-------------+-------+------+---------------------------------+-------------+---------+-------------------------------------------------------------+-------+---------------------------------+

使用的索引(w2pibnpage1)打开:

word2,pibn,page,word1,id

我一直在努力解决这个问题,为索引尝试不同的列组合(这很烦人,因为重建需要一个小时 - 数百万行)。

我的索引应该是什么,或者我该怎么做才能让这个查询在几分之一秒内运行(应该如此)?

5 个答案:

答案 0 :(得分:0)

这是一个建议。

据推测,临时表很小。您可以删除该表上的索引,因为那里的全表扫描很好。事实上,这就是你想要的。

然后你想要在大表上使用索引。首先,索引需要匹配连接条件,然后匹配where条件,最后匹配group by条件。所以,建议是:

mytable(pibn, page, word2, word1, aaa)

我正在使用order by列,因此无需从原始数据中获取值。

答案 1 :(得分:0)

如果你的temptbl很小,你想首先限制更大的表(mytable),然后将它(最终通过索引)加入你的temptbl。

目前,MySQL认为通过使用较大表的索引来加入会更好。

你可以通过直接加入解决这个问题:

  SELECT word1,COUNT(*) AS aaa
    FROM mytable a
    STRAIGHT_JOIN temptbl b
      ON a.pibn=b.pibn AND a.page=b.page
  WHERE word2=0
  GROUP BY word1 
  ORDER BY aaa DESC LIMIT 10;

这应该使用mytable中的索引作为where子句,并通过temptbl中的索引将mytable连接到temptbl。

如果MySQL仍然想要做的不同,你可以使用FORCE INDEX使其使用索引。

答案 2 :(得分:0)

查询花了很长时间,但昂贵的部分似乎是访问'mytable'(你没有提供这个结构)但是优化器似乎认为它只需要从中获取4行使用index - 应该非常快。即数据似乎非常偏斜 - 最后一个查询检查了多少行(计数计数)?

如果没有精确的数据分布,很难确定 - 当然,您可能需要提示查询以使其有效工作。设计索引的问题在于它们应该更快地所有查询 - 或者至少给出合理的权衡。

查看您提供的查询中的谓词...

WHERE word1=429907 AND word2=0

最好通过word1,word2,....或word2,word1,.....的索引服务。

ON a.pibn=b.pibn AND a.page=b.page
WHERE a.word2=0

最好通过mytable上的索引与word2 + pibn +页面在前导列中提供最佳服务。

mytable.word1和mytable.word2有多少个不同的值?如果word2具有较少数量的不同值(小于20左右),则它不会对索引添加太多选择性,可以省略。

word2,pibn,page,word1上的索引为您提供第二个查询的覆盖索引。

答案 3 :(得分:0)

无论您做什么,您的数据量都不会快速运行,而不是不更改架构。

如果我理解正确,您就会在同一页面上寻找与429907一起出现的热门词汇。

你建模,因为它现在需要在每次运行查询时重复计算所有这些单词。

要加快速度,您需要创建一个额外的统计表:

CREATE TABLE word_pairs
        (
        word1_1 INT NOT NULL,
        word1_2 INT NOT NULL,
        cnt BIGINT NOT NULL,
        PRIMARY KEY (word1_1, word1_2),
        INDEX (word1_1, cnt),
        INDEX (word1_2, cnt)
        )

并在每次将记录插入大表时更新它(为新插入的单词增加cnt,并将其与所有单词放在同一页面上)。

对于单个服务器来说这可能太慢了,因为这样的更新需要一些时间,因此您还需要在多个服务器上对该表进行分片。

如果你有这样的表,你可以运行:

SELECT  *
FROM    word_pairs
WHERE   word1_1 = 429907
ORDER BY
        cnt DESC
LIMIT   10

这将是即时的。

答案 4 :(得分:0)

我想出了这个:

CREATE TEMPORARY TABLE temp1 (
  pibn INT UNSIGNED NOT NULL, page SMALLINT UNSIGNED NOT NULL)
  ENGINE=MEMORY;
INSERT INTO temp1 (
  SELECT pibn,page FROM mytable
  WHERE word1=429907 AND word2=0);
CREATE TEMPORARY TABLE temp2 (
  word1 MEDIUMINT UNSIGNED NOT NULL)
  ENGINE=MEMORY;
INSERT INTO temp2 (
SELECT a.word1
  FROM mytable a, temp1 b
  WHERE a.word2=0 AND a.pibn=b.pibn AND a.page=b.page);
DROP TABLE temp1;
CREATE INDEX index1 ON temp2 (word1) USING BTREE;
CREATE TEMPORARY TABLE temp3 (
  word1 MEDIUMINT UNSIGNED NOT NULL, num INT UNSIGNED NOT NULL)
  ENGINE=MEMORY;
INSERT INTO temp3 (SELECT word1,COUNT(*) AS aaa FROM temp2 USE INDEX (index1) GROUP BY word1);
DROP TABLE temp2;
CREATE INDEX index1 ON temp3 (num) USING BTREE;
SELECT word1,num FROM temp3 USE INDEX (index1) ORDER BY num DESC LIMIT 10;
DROP TABLE temp3;

需要5秒钟。