【问题标题】:MySQL taking forever 'sending data'. Simple query, lots of dataMySQL 永远“发送数据”。查询简单,数据量大
【发布时间】:2012-04-11 04:01:58
【问题描述】:

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

谢谢。

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

【问题讨论】:

    标签: mysql performance query-optimization large-data


    【解决方案1】:

    您为什么不分析查询并查看到底发生了什么?

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • +1。添加另一个具有前导列created 的索引会使created 列上的OP 索引变得多余;可以删除仅 created 列上的索引。交换了protocolcreated 列的不同覆盖索引也可能会提高性能。在这种情况下,protocol 列上的单独索引将是多余的。
      【解决方案3】:

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

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

      【讨论】:

      • “覆盖索引”最有帮助(这个特定查询的性能)。权衡是维护索引所需的资源。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-28
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-23
      相关资源
      最近更新 更多