2012-04-11 56 views
1

我试图运行我认为是一个相当大的数据集上的简单查询,它需要很长时间才能执行 - 它停止在“发送数据”状态3 -4小时以上。MySQL永远在“发送数据”。简单的查询,大量的数据

表看起来是这样的:

CREATE TABLE `transaction` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`uuid` varchar(36) NOT NULL, 
`userId` varchar(64) NOT NULL, 
`protocol` int(11) NOT NULL, 
... A few other fields: ints and small varchars 
`created` datetime NOT NULL, 
PRIMARY KEY (`id`), 
KEY `uuid` (`uuid`), 
KEY `userId` (`userId`), 
KEY `protocol` (`protocol`), 
KEY `created` (`created`) 
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4 COMMENT='Transaction audit table' 

和查询是在这里:

select protocol, count(distinct userId) as count from transaction 
where created > '2012-01-15 23:59:59' and created <= '2012-02-14 23:59:59' 
group by protocol; 

表有大约2.22亿行,并在查询的WHERE子句过滤下降到约20百万行。不同的选项会将其降至大约700,000个不同的行,然后在分组后(以及查询最终完成时),实际返回4到5行。

我意识到这是很多的数据,但似乎4-5小时是这个查询非常长的时间。

谢谢。

编辑:作为参考,它在AWS上运行在db.m2.4xlarge RDS数据库实例上。

回答

3

这是一个非常重的查询。要理解为什么需要这么长时间,你应该了解细节。

在索引字段中有一个范围条件,即MySQL在索引中找到最小的创建值,并且每个值都从索引获取相应的主键,从磁盘检索该行并获取必需的字段(protocol,userId)在当前索引记录中丢失,将它们放在一个“临时表”中,对这些700000行进行分组。该索引实际上可以使用,仅用于加快范围条件。

加速它的唯一方法是有一个包含所有必要数据的索引,这样MySQL就不需要对行的磁盘查找进行操作。这就是所谓的covering index。但是您应该明白,索引将驻留在内存中,并且将包含〜sizeOf(created+protocol+userId+PK)*rowCount个字节,这可能会成为更新表和其他索引的查询本身的负担。创建单独的聚合表并使用查询定期更新表更容易。

+0

+1。添加另一个带有“created”列的索引会使OP索引在“created”列冗余;只有'created'列的索引可以被删除。有可能一个不同的覆盖索引,'协议'和'创建'列交换,也可能会提高性能。在这种情况下,只有'protocol'列的单独索引是多余的。 – spencer7593 2014-02-14 23:10:09

1

不同的和不同的将需要排序和存储服务器上的临时数据。有了那么多的数据可能需要一段时间。

对userId,创建的和协议的不同组合进行索引将有所帮助,但我不能说多少或哪些索引对大多数人有帮助。

+0

'覆盖索引'将有助于(这个特定查询的执行)最多。权衡是维持指数所需的资源。 – spencer7593 2014-02-14 23:07:21

11

为什么不分析查询并查看究竟发生了什么?

SET PROFILING = 1; 
SET profiling_history_size = 0; 
SET profiling_history_size = 15; 
/* Your query should be here */ 
SHOW PROFILES; 
SELECT state, ROUND(SUM(duration),5) AS `duration (summed) in sec` FROM information_schema.profiling WHERE query_id = 3 GROUP BY state ORDER BY `duration (summed) in sec` DESC; 
SET PROFILING = 0; 
EXPLAIN /* Your query again should appear here */; 

我认为这将有助于你在看哪儿查询需要一定的时间,并根据结果可以执行优化操作。