【问题标题】:Very slow MYSQL query for 2.5 million row table250万行表的MYSQL查询非常慢
【发布时间】:2010-12-19 22:42:49
【问题描述】:

我真的很难缩短查询时间,它目前必须查询 250 万行并且需要 20 多秒

这里是查询

SELECT play_date AS date, COUNT(DISTINCT(email)) AS count
FROM log
WHERE play_date BETWEEN '2009-02-23' AND '2020-01-01'
AND type = 'play'
GROUP BY play_date
ORDER BY play_date desc;

 `id` int(11) NOT NULL auto_increment,
  `instance` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `type` enum('play','claim','friend','email') NOT NULL,
  `result` enum('win','win-small','lose','none') NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `play_date` date NOT NULL,
  `email_refer` varchar(255) NOT NULL,
  `remote_addr` varchar(15) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `email` (`email`),
  KEY `result` (`result`),
  KEY `timestamp` (`timestamp`),
  KEY `email_refer` (`email_refer`),
  KEY `type_2` (`type`,`timestamp`),
  KEY `type_4` (`type`,`play_date`),
  KEY `type_result` (`type`,`play_date`,`result`)

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  log ref type_2,type_4,type_result   type_4  1   const   270404  Using where

查询正在使用 type_4 索引。

有谁知道如何加快这个查询速度?

谢谢 汤姆

【问题讨论】:

  • +1 用于提供所有相关信息和清晰的解释。
  • 你能运行一个没有 COUNT 字段的函数来看看查询速度有多大变化吗?并不是说您不需要该字段,只是作为基准来了解它的影响有多大。
  • 不计时间缩短了大约 8 秒
  • 尝试按此顺序为 ("play_date", "email") 创建一个复合索引,如果使用它应该确实可以加快查询速度。
  • 看起来像将电子邮件添加到 type_4 索引修复它

标签: mysql optimization


【解决方案1】:

已经相对不错了。性能下降是查询必须比较 270404 个 varchars 是否与 COUNT(DISTINCT(email)) 相等,这意味着必须读取 270404 行。

您可以通过创建覆盖索引来加快计数速度。这意味着不需要读取实际行,因为所有必需的信息都存在于索引本身中。

为此,请按如下方式更改索引:

KEY `type_4` (`type`,`play_date`, `email`)

如果这不能加快速度,我会感到惊讶。

(感谢 MarkR 提供正确的术语。)

【讨论】:

  • 刚刚试了一下,有了很大的改进,查询时间现在不到 3 秒,我们可以忍受。非常感谢!
  • 我认为对于这个特定的查询,删除类型也会有所帮助。
  • 这称为“覆盖索引”;当查询中使用的所有列都在索引中时,就会发生这种情况。在这种情况下,它们也恰好处于正确的顺序,因此 GROUP BY 可以优化为覆盖索引的简单范围扫描(不需要排序),这是一种非常好的查询计划类型,即使有相当多的很多行;数据的局部性会很好。
【解决方案2】:

COUNT(DISTINCT(email)) 部分是要杀死你的部分。如果您真的只需要 270,404 的前 2000 个结果,那么仅对结果而不是整个集合进行电子邮件计数可能会有所帮助。

SELECT date, COUNT(DISTINCT(email)) AS count
FROM log,
(
    SELECT play_date AS date
      FROM log
     WHERE play_date BETWEEN '2009-02-23' AND '2020-01-01'
       AND type = 'play'
     ORDER BY play_date desc
     LIMIT 2000
) AS shortlist
WHERE shortlist.id = log.id
GROUP BY date

【讨论】:

  • ... 除了他说 count() 片段只会杀死他 20 秒中的 8 秒。
【解决方案3】:

表扫描很有可能比随机访问超过 200,000 行更快:

SELECT ... FROM log IGNORE INDEX (type_2,type_4,type_result) ...

此外,对于大型分组查询,通过强制文件排序而不是基于哈希表的组,您可能会看到更好的性能(因为如果这需要更多 tmp_table_sizemax_heap_table_size 性能崩溃):

SELECT SQL_BIG_RESULT ...

【讨论】:

    【解决方案4】:

    从长远来看,我建议使用 play_date 的主键和不同电子邮件的计数来构建一个汇总表。

    取决于您需要它的最新程度 - 允许它每天更新(按 play_date)或通过日志表上的触发器实时更新。

    【讨论】:

      【解决方案5】:

      在 play_date 上尝试索引,输入(与 type_4 相同,只是反转字段),看看是否有帮助

      有 4 种可能的类型,我假设有 100 种可能的日期。如果查询使用类型,play_date 索引,它基本上(不是 100% 准确,而是一般的想法)说。

      (A) Find all the Play records (about 25% of the file)
      (B) Now within that subset, find all of the requested dates
      

      通过反转索引,方法是

      > (A) Find all the dates within range
      > (Maybe 1-2% of file) (B) Now find all
      > PLAY types within that smaller portion
      > of the file
      

      希望对你有帮助

      【讨论】:

      • 这个答案将节省使用另一个索引的需要,但可能会影响其他依赖于类型而不是 play_date 的查询。这是一种权衡。
      • 嗨 Sparky,我已经创建了新索引,但 MySQL 不会使用它,所以我使用了 FORCE INDEX,但哪里似乎返回了所有行? id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE log range play_date_type play_date_type 4 NULL 2519022 使用where
      【解决方案6】:

      将电子邮件提取到单独的表应该可以很好地提高性能,因为计算不同的 varchar 字段需要一段时间。除此之外 - 使用了正确的索引,并且查询本身已尽可能优化(当然,电子邮件除外)。

      【讨论】:

        【解决方案7】:

        您的索引可能与您所能获得的一样好。您在 where 子句中的 2 列上有一个复合索引,并且您发布的 explain 表明它正在被使用。不幸的是,有 270,404 行符合您的 where 子句中的条件,它们都需要考虑。此外,您不会在 select 列表中返回不必要的行。

        我的建议是每天(或每小时或任何有意义的方式)汇总数据并缓存结果。这样您就可以立即访问稍微陈旧的数据。希望这对于您的目的是可以接受的。

        【讨论】:

        • 不幸的是统计数据需要实时
        【解决方案8】:

        尝试仅在 play_date 创建索引。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-09
          • 1970-01-01
          • 2011-11-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多