这是我的问题:
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
我一直在努力解决这个问题,为索引尝试不同的列组合(这很烦人,因为重建需要一个小时 - 数百万行)。
我的索引应该是什么,或者我该怎么做才能让这个查询在几分之一秒内运行(应该如此)?
答案 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秒钟。