非常慢的MYSQL查询250万行表

时间:2009-11-24 15:59:15

标签: 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

查询使用type_4索引。

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

由于 汤姆

8 个答案:

答案 0 :(得分:18)

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

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

要执行此操作,请按如下所示更改索引:

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

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

(感谢MarkR的正确任期。)

答案 1 :(得分:5)

你的索引可能和你能得到的一样好。您的where子句中的2列上有复合索引,而您发布的explain表示正在使用它。不幸的是,有270,404行符合where子句中的条件,所有这些行都需要考虑。此外,您还没有在select列表中返回不必要的行。

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

答案 2 :(得分:4)

在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

希望这有帮助

答案 3 :(得分:3)

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

尝试仅在play_date上创建索引。

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

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

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

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

SELECT SQL_BIG_RESULT ...