3

这是我的查询:

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,特别是计数。该选择语句需要 16 秒。

解释 说:

+----+-------------+-------+------+---------------------------------+-------------+---------+-------------------------------------------------------------+-------+---------------------------------+
| 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

我已经为此苦苦挣扎了好几天,为索引尝试了不同的列组合(这很烦人,因为重建需要一个小时 - 数百万行)。

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

4

5 回答 5

0

这是一个建议。

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

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

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

我正在order by列中,因此它不必从原始数据中获取值。

于 2013-04-16T11:05:02.050 回答
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 让它使用索引。

于 2013-04-16T11:24:12.760 回答
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

这将是即时的。

于 2013-04-16T11:48:56.357 回答
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 秒。

于 2013-04-16T12:53:27.500 回答
0

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

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

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

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+page 在前导列中。

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

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

于 2013-04-16T11:26:56.940 回答