【问题标题】:Optimizing MySQL Aggregation Query优化 MySQL 聚合查询
【发布时间】:2010-10-23 04:27:26
【问题描述】:

我在 MySQL 中有一个非常大的表(约 1 亿条记录),其中包含有关文件的信息。其中一条信息是每个文件的修改日期。

我需要编写一个查询来计算适合指定日期范围的文件数。为此,我制作了一个小表来指定这些范围(均以天为单位),如下所示:

DateRanges
range_id   range_name   range_start   range_end
1          0-90         0             90
2          91-180       91            180
3          181-365      181           365
4          366-1095     366           1095
5          1096+        1096          999999999

并写了一个如下所示的查询:

SELECT r.range_name, sum(IF((DATEDIFF(CURDATE(),t.file_last_access) > r.range_start and DATEDIFF(CURDATE(),t.file_last_access) < r.range_end),1,0)) as FileCount
FROM `DateRanges` r, `HugeFileTable` t
GROUP BY r.range_name

但是,可以预见的是,这个查询需要很长时间才能运行。我认为这是因为我要求 MySQL 遍历 HugeFileTable 5 次,每次对每个文件执行 DATEDIFF() 计算。

我想要做的是仅逐条记录 HugeFileTable 记录一次,并为每个文件增加相应 range_name 运行总数中的计数。我不知道该怎么做....

有人可以帮忙吗?

谢谢。

编辑:MySQL 版本:5.0.45,表是 MyISAM

EDIT2:这是 cmets 中要求的描述

id  select_type  table  type  possible_keys  key  key_len  ref  rows      Extra  
1   SIMPLE       r      ALL   NULL           NULL NULL     NULL 5         Using temporary; Using filesort 
1   SIMPLE       t      ALL   NULL           NULL NULL     NULL 96506321   

【问题讨论】:

  • 你用的是什么版本的 MySQL?
  • 您能否在查询前运行 DESCRIBE 并将输出结果提供给我们,以便我们查看是否有任何需要改进的地方

标签: mysql aggregate large-data-volumes


【解决方案1】:

好吧,首先确保file_last_access 是表HugeFileTable索引

我不确定这是否可能\更好,但请先尝试计算日期限制(从日期 A 到日期 B 的文件),然后使用一些使用 >= 和

比较类似于:

 t.file_last_access >= StartDate AND t.file_last_access <= EndDate 

【讨论】:

  • 感谢您的回复。我认为这不会极大地提高性能,它会进行一次比较(以天为单位),但这并不是所有减速的真正来源。另外,我无法将该列设为索引,但我不知道它有什么帮助。
  • @zenshai 使用索引(B-tree)通过允许 mysql 基本上提前放弃任何超出您所需范围的 file_last_access 值来加速查询。如果没有索引,您将进行 O(N) 比较的表扫描,其中 N 是表中的行数。使用索引,您可以执行 O(M),其中 M 是匹配行数,M
  • @mattkemp:啊,我明白你在说什么,我在考虑我的原始查询,而不是 Aziz 建议的这个修改。问题是在这些巨大的表上创建索引需要很长时间并且占用大量空间,而且我不仅要在上次访问日期而且还要在修改日期和创建日期上运行相同的查询,所以我需要那些索引也是。
【解决方案2】:

您可以通过删除 CURDATE() 并在查询中添加一个日期来获得小幅改进,因为它将在您的 SQL 中为每一行运行两次此函数。

【讨论】:

    【解决方案3】:

    首先,在HugeFileTable.file_last_access上创建一个索引。

    然后尝试以下查询:

    SELECT r.range_name, COUNT(t.file_last_access) as FileCount
    FROM `DateRanges` r
     JOIN `HugeFileTable` t 
     ON (t.file_last_access BETWEEN 
       CURDATE() + INTERVAL r.range_start DAY AND 
       CURDATE() + INTERVAL r.range_end DAY)
    GROUP BY r.range_name;
    

    这是我在 MySQL 5.0.75 上尝试此查询时得到的 EXPLAIN 计划(为简洁起见已编辑):

    +-------+-------+------------------+----------------------------------------------+
    | table | type  | key              | Extra                                        |
    +-------+-------+------------------+----------------------------------------------+
    | t     | index | file_last_access | Using index; Using temporary; Using filesort | 
    | r     | ALL   | NULL             | Using where                                  | 
    +-------+-------+------------------+----------------------------------------------+
    

    它仍然不会表现得很好。通过使用GROUP BY,查询会产生一个临时表,这可能很昂贵。您对此无能为力。

    但至少这个查询消除了您在原始查询中的笛卡尔积。


    更新:这是另一个使用相关子查询的查询,但我删除了GROUP BY

    SELECT r.range_name,
      (SELECT COUNT(*) 
       FROM `HugeFileTable` t 
       WHERE t.file_last_access BETWEEN 
         CURDATE() - INTERVAL r.range_end DAY AND 
         CURDATE() - INTERVAL r.range_start DAY
      ) as FileCount
    FROM `DateRanges` r;
    

    EXPLAIN 计划没有显示临时表或文件排序(至少在我的测试表中的行数很少):

    +----+--------------------+-------+-------+------------------+--------------------------+
    | id | select_type        | table | type  | key              | Extra                    |
    +----+--------------------+-------+-------+------------------+--------------------------+
    |  1 | PRIMARY            | r     | ALL   | NULL             |                          | 
    |  2 | DEPENDENT SUBQUERY | t     | index | file_last_access | Using where; Using index | 
    +----+--------------------+-------+-------+------------------+--------------------------+
    

    在您的数据集上尝试此查询,看看它是否表现更好。

    【讨论】:

    • 所以我在没有索引的情况下运行了您的查询(如果不需要的话,我真的不想做一个),它在 9182 秒(2.5 小时)内完成,这实际上是可以接受的,因为生病了大约每周一次通宵运行它。所以,非常感谢您的帮助。
    • 好的,只要是可以接受的,这就是你的决定。我建议尝试使用索引进行比较。如果它没有明显更快,那么您总是可以在该测试之后删除索引。
    猜你喜欢
    • 2010-12-18
    • 2021-09-03
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-18
    相关资源
    最近更新 更多