我真的很难让查询时间缩短,目前它不得不查询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索引。
有谁知道如何加快查询速度?
由于 汤姆
答案 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_size
或max_heap_table_size
性能崩溃,则:
SELECT SQL_BIG_RESULT ...