我试图运行我认为对一个相当大的数据集进行简单查询,并且需要花费很长时间才能执行 - 它会停留在"发送数据和#34;状态3-4小时或更长时间。
表格如下:
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子句过滤到大约2000万行。 distinct选项会将其降低到大约700,000个不同的行,然后在分组后(以及查询最终完成时),实际返回4到5行。
我意识到它有很多数据,但似乎4-5小时对于这个查询来说是非常长的时间。
感谢。
编辑:作为参考,这是在db.m2.4xlarge RDS数据库实例上的AWS上运行。
答案 0 :(得分: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 */;
我认为这可以帮助您查看确切查询需要时间的位置,并根据结果执行优化操作。
答案 1 :(得分:3)
这是一个非常繁重的查询。要理解为什么需要这么长时间,你应该了解细节。
索引字段上有一个范围条件,即MySQL在索引中找到最小的创建值,并且每个值从索引获取相应的主键,从磁盘检索行,并获取所需的字段(当前索引记录中缺少protocol,userId),将它们放在&#34;临时表&#34;中,对这些700000行进行分组。该索引实际上可以使用,此处仅用于加速范围条件。
加速它的唯一方法是拥有一个包含所有必要数据的索引,这样MySQL就不需要对行进行磁盘查找。这被称为covering index
。但是您应该理解索引将驻留在内存中并且将包含〜sizeOf(created+protocol+userId+PK)*rowCount
个字节,这可能会成为更新表和其他索引的查询的负担。创建单独的聚合表并使用您的查询定期更新表更容易。
答案 2 :(得分:1)
distinct和group by都需要在服务器上对临时数据进行排序和存储。有这么多数据可能需要一段时间。
索引userId,created和protocol的不同组合会有所帮助,但我不能说多少或哪个索引对你有帮助。