非常慢的MYSQL查询250万行表

Tom*_*Tom 22 mysql optimization

我真的很难让查询时间缩短,它目前不得不查询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
Run Code Online (Sandbox Code Playgroud)

该查询使用type_4索引.

有谁知道我怎么能加快这个查询速度?

谢谢汤姆

Hen*_*ing 18

这已经相对不错了.性能接收器是查询必须比较270404 varchars的相等性COUNT(DISTINCT(email)),这意味着必须读取270404行.

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

为此,请按如下所示更改索引:

KEY `type_4` (`type`,`play_date`, `email`)
Run Code Online (Sandbox Code Playgroud)

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

(感谢MarkR的正确任期.)

  • 这被称为"覆盖指数"; 当查询中使用的所有列都在索引中时,就会发生这种情况.在这种情况下,它们也恰好是正确的顺序,以便GROUP BY可以优化为覆盖索引的简单范围扫描(不需要排序),这是一个非常好的查询计划类型,即使有相当的很多行; 将有良好的数据位置. (6认同)
  • 刚尝试了这个并获得了巨大的改进,查询时间现在不到3秒,我们可以忍受.非常感谢你! (2认同)

Asa*_*aph 5

你的索引可能和你能得到的一样好.您在where子句中的2列上有一个复合索引,并且explain您发布的表明它正在使用.不幸的是,有270,404行符合您的where条款中的标准,所有这些都需要考虑.此外,您不会在select列表中返回不必要的行.

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