如何通过已使用索引的查询加快组的速度?

时间:2012-02-07 11:01:24

标签: mysql

我们有一个MyISAM表,大约有75百万行,有5列:

id (int), 
user_id(int), 
page_id (int), 
type (enum with 6 strings)
date_created(datetime).

我们在ID列上有一个主索引,一个唯一索引(user_id,page_id,date_created)和一个复合索引(page_id,date_created)

问题是以下查询最多需要90秒才能完成

SELECT SQL_NO_CACHE user_id, count(id) nr 
FROM `table` 
WHERE `page_id`=301 
and `date_created` BETWEEN '2012-01-03' AND '2012-02-03 23:59:59' 
AND page_id<>user_id 
group by `user_id`

这是此查询的解释

+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
| id | select_type | table                      | type  | possible_keys | key     | key_len | ref  | rows   | Extra                                        |
+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+
|  1 | SIMPLE      | table                      | range | page_id       | page_id | 12      | NULL | 520024 | Using where; Using temporary; Using filesort |
+----+-------------+----------------------------+-------+---------------+---------+---------+------+--------+----------------------------------------------+

修改 在ypercube的建议下,我尝试添加一个新索引(page_id,user_id,date_created)。但是mysql不使用它默认,所以我不得不建议查询优化器。这是新查询和解释:

SELECT SQL_NO_CACHE user_id, count(*) nr FROM `table` USE INDEX (usridexp) WHERE `page_id`=301 and `date_created` BETWEEN '2012-01-03' AND '2012-02-03 23:59:59' AND page_id<>user_id group by `user_id` ORDER BY NULL


    +----+-------------+----------------------------+------+---------------+----------+---------+-------+---------+--------------------------+
    | id | select_type | table                      | type | possible_keys | key      | key_len | ref   | rows    | Extra                    |
    +----+-------------+----------------------------+------+---------------+----------+---------+-------+---------+--------------------------+
    |  1 | SIMPLE      | table                      | ref  | usridexp      | usridexp | 4       | const | 3943444 | Using where; Using index |
    +----+-------------+----------------------------+------+---------------+----------+---------+-------+---------+--------------------------+

3 个答案:

答案 0 :(得分:6)

可能会改进查询的一些更改:

  • COUNT(id)更改为COUNT(*)。由于id是(我猜)PRIMARY KEY和NOT NULL,结果将是相同的。

  • ORDER BY NULL子句之后添加GROUP BY。在MySQL中,按操作分组也会对结果进行排序,除非您指定其他方式。

  • (page_id, date_created)可能是MySQL可用于此查询的最佳索引,但您也可以尝试(page_id, user_id, date_created)(如果添加此索引,还可以发布EXPLAIN吗?)


与此查询的性能无关的另一件事:

如果您(user_id, page_id, date_created)UNIQUE并且id是自动生成的(并且除了作为主键之外没有用于其他任何内容),您可以将其设为PRIMARY KEY并删除id列。少一个索引,每行少4个字节。

答案 1 :(得分:0)

1)这取决于您的数据 - 但您应该有多个索引可供MySQL选择最佳索引。例如如果表在page_id上​​有索引,则不会扫描这么多行。

2)有一种优化日期搜索的方法。我自己还没有真正实现过这个问题,但是我遇到了类似的问题。

基本上你是按天查找数据 - 但是日期比较非常慢。你可以做的是创建另一个表,每天从表中存储最早和最新的ID。该表需要在每天结束时填充。

之后,您可以将查询分为两部分:

i)找到要搜索的ID以及运行两个查询: 从idCacheTable中选择earliestID,其中date ='2012-01-03'; 从idCacheTable中选择latestID,其中date ='2012-02-03';

ii)然后,您可以直接搜索表格的主键,而无需在每一行上进行日期比较,这样可以更快地进行日期比较。

SELECT SQL_NO_CACHE user_id,count(id)nr 来自table WHERE page_id = 301 和(id&gt; = earliestID和id&lt; = latestID) AND page_id&lt;&gt; user_id 分组user_id;

问题的确切解决方案将取决于您的数据是什么样的,而不是这两件事中的一个总是正确的。

答案 2 :(得分:0)

听起来很奇怪,但尝试添加JOIN语句:

SELECT SQL_NO_CACHE user_id, count(id) nr
FROM `table` t
JOIN `table` t2 ON t.`user_id`= t2.`user_id`
WHERE t.`page_id`=301
and t.`date_created` BETWEEN '2012-01-03' AND '2012-02-03 23:59:59'
AND t.`page_id`<>t.`user_id`
group by t.`user_id`


对于类似的问题,我得到的查询执行速度提高了20倍(3-4s而不是60+)。 JOIN语句不执行任何智能 - 似乎加速完全是内部MySql实现(在MySql 5.1上测试。,表有罕见的user_id重复)。