MySQL的到永远“发送数据”。 简单的查询,大量数据(MySQL taking forever

2019-10-16 14:49发布

我试图跑什么,我认为是一个相当大的数据集的简单查询,它采取了很长的时间来执行 - 它档中的“发送数据”状态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子句过滤到约20万行。 在不同的选项将使其下降到约700,000不同的行,然后分组后,(当查询终于完成),实际上是回到4〜5行。

我意识到,这是一个很大的数据,但似乎4-5小时是一个相当长的时间,这个查询。

谢谢。

编辑:作为参考,这是对一个db.m2.4xlarge RDS数据库实例的AWS上运行。

Answer 1:

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

你对索引字段的范围内的条件,即MySQL的发现在索引最小的创建值和它得到从索引相应的主键的每个值,检索来自盘的行,并提取所需的字段(协议,用户id )在当前索引记录缺失,使他们在一个“临时表”,使得那些70万行的分组。 该指数实际上可以使用,在这里仅用于加快范围条件。

加快步伐,唯一的办法是有一个包含所有必要的数据,从而使MySQL的就不需要做对行磁盘上的查询索引。 这被称为covering index 。 但你应该明白,该指数将驻留在内存中,并且将包含〜 sizeOf(created+protocol+userId+PK)*rowCount字节,这可能成为一种负担,因为自己对于更新表的查询和其他指标。 这是比较容易创建一个单独的聚合表,并使用您的查询定期更新表。



Answer 2:

你为什么不简档的问询,看看究竟是怎么回事?

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

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



Answer 3:

这两种截然不同的和group by需要排序和临时数据存储在服务器上。 与可能需要一段时间,如此多的数据。

用户id的索引的不同组合,创造和协议将帮助,但我不能说有多少或什么指标能帮助最多。



文章来源: MySQL taking forever 'sending data'. Simple query, lots of data