如何优化在大量行上使用group by的查询

时间:2010-10-06 17:40:14

标签: mysql indexing

表格如下:

    CREATE TABLE `tweet_tweet` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `text` varchar(256) NOT NULL,
      `created_at` datetime NOT NULL,
      `created_date` date NOT NULL,
...
      `positive_sentiment` decimal(5,2) DEFAULT NULL,
      `negative_sentiment` decimal(5,2) DEFAULT NULL,
      `entity_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `tweet_tweet_entity_created` (`entity_id`,`created_at`)
    ) ENGINE=MyISAM AUTO_INCREMENT=1097134 DEFAULT CHARSET=utf8

查询的解释如下:

mysql> explain SELECT `tweet_tweet`.`entity_id`, 
       STDDEV_POP(`tweet_tweet`.`positive_sentiment`) AS `sentiment_stddev`, 
       AVG(`tweet_tweet`.`positive_sentiment`) AS `sentiment_avg`, 
       COUNT(`tweet_tweet`.`id`) AS `tweet_count` 
       FROM `tweet_tweet` 
       WHERE `tweet_tweet`.`created_at` > '2010-10-06 16:24:43'  
       GROUP BY `tweet_tweet`.`entity_id` ORDER BY `tweet_tweet`.`entity_id` ASC;

+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+
| id | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra                                        |
+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | tweet_tweet | ALL  | NULL          | NULL | NULL    | NULL | 1097452 | Using where; Using temporary; Using filesort |
+----+-------------+-------------+------+---------------+------+---------+------+---------+----------------------------------------------+
  1 row in set (0.00 sec)

每天大约有300,000行添加到表中。该查询现在运行大约4秒,但我想将其降低到大约1秒钟,我担心随着时间的推移,查询将呈指数级增长。 tweet_tweet中的总行数目前只有1M多一点,但它会快速增长。

有关优化此事的任何想法?我还需要更多索引吗?我应该使用像Cassandra而不是MySQL吗? =)

4 个答案:

答案 0 :(得分:1)

您可以尝试重新排序索引中的字段(即KEY tweet_tweet_entity_created (created_at, entity_id)。这将允许mysql使用索引来减少需要分组和排序的实际行数。)

答案 1 :(得分:0)

你没有使用索引tweet_tweet_entity_created。将您的查询更改为:

explain SELECT `tweet_tweet`.`entity_id`, 
       STDDEV_POP(`tweet_tweet`.`positive_sentiment`) AS `sentiment_stddev`, 
       AVG(`tweet_tweet`.`positive_sentiment`) AS `sentiment_avg`, 
       COUNT(`tweet_tweet`.`id`) AS `tweet_count` 
       FROM `tweet_tweet` FORCE INDEX (tweet_tweet_entity_created)
       WHERE `tweet_tweet`.`created_at` > '2010-10-06 16:24:43'  
       GROUP BY `tweet_tweet`.`entity_id` ORDER BY `tweet_tweet`.`entity_id` ASC;

您可以在MySQL手册http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

中阅读有关索引提示的更多信息

有时MySQL的查询优化器需要一些帮助。

答案 2 :(得分:0)

MySQL有一个肮脏的小秘密。在多列上创建索引时,只有第一列真正被“使用”。我创建了使用Unique Keys和Foreign Keys的表,我经常需要为一个或多个列设置单独的索引。

我建议至少在created_at中添加一个额外的索引。我不知道在聚合列中添加索引是否也会加快速度。

答案 3 :(得分:0)

如果您的mysql版本为5.1或更高版本,则可以考虑对大型表进行分区选项。

http://dev.mysql.com/doc/refman/5.1/en/partitioning.html