1

我正在尝试在一个相当大的数据集上运行我认为是一个简单的查询,并且它需要很长时间才能执行——它在“发送数据”状态下停滞 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 上运行的。

4

3 回答 3

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 */;

我认为这将帮助您查看查询需要时间的确切位置,并根据结果您可以执行优化操作。

于 2012-04-11T05:15:54.163 回答
3

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

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

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

于 2012-04-11T08:12:46.573 回答
1

distinct 和 group by 都需要对服务器上的临时数据进行排序和存储。有这么多数据可能需要一段时间。

索引 userId、created 和 protocol 的不同组合会有所帮助,但我不能说多少或什么索引最有帮助。

于 2012-04-11T04:10:26.703 回答