2012-04-27 131 views
0

我有一张超过2.5亿条记录的表。我们的报告服务器使用类似的查询定期查询该表。MySQL查询永久运行

SELECT 
    COUNT(*), 
    DATE(updated_at) AS date, 
    COUNT(DISTINCT INT_FIELD) 
FROM 
    TABLE_WITH_250_Million 
WHERE 
    Field1 = 'value in CHAR' 
    AND field2 = 'VALUE in CHAR' 
    AND updated_at > '2012-04-27' 
    AND updated_at < '2012-04-28 00:00:00' 
GROUP BY 
    Field2, 
    DATE(updated_at) 
ORDER BY 
    date DESC 

我试图创建表,包括字段1,字段2,字段3 DESC以相同的顺序,但它不是给我正确的结果B树索引。

任何人都可以帮助我如何优化它。我的问题是我无法更改查询,因为我没有代码在这个报表服务器正在执行查询。

任何帮助将非常感激。

感谢


这里是我的表:

CREATE TABLE backup_jobs ( 
    id int(11) unsigned NOT NULL AUTO_INCREMENT, 
    backup_profile_id int(11) DEFAULT NULL, 
    state varchar(32) DEFAULT NULL, 
    limit int(11) DEFAULT NULL, 
    file_count int(11) DEFAULT NULL, 
    byte_count bigint(20) DEFAULT NULL, 
    created_at datetime DEFAULT NULL, 
    updated_at datetime DEFAULT NULL, 
    status_type varchar(32) DEFAULT NULL, 
    status_param_1 varchar(255) DEFAULT NULL, 
    status_param_2 varchar(255) DEFAULT NULL, 
    status_param_3 varchar(255) DEFAULT NULL, 
    started_at datetime DEFAULT NULL, 
    PRIMARY KEY (id), 
    KEY index_backup_jobs_on_state (state), 
    KEY index_backup_jobs_on_backup_profile_id (backup_profile_id), 
    KEY index_backup_jobs_created_at (created_at), 
    KEY idx_backup_jobs_state_updated_at (state,updated_at) USING BTREE, 
    KEY idx_backup_jobs_state_status_param_1_updated_at (state,status_param_1,updated_at) USING BTREE 
) ENGINE=MyISAM AUTO_INCREMENT=508748682 DEFAULT CHARSET=utf8; 
+4

发布表定义('SHOW CREATE TABLE tablename')和查询的解释('EXPLAIN SELECT count(*)...')。 – 2012-04-27 15:50:49

+0

“它不给我正确的结果”:它是一个“CREATE INDEX”,它没有给出正确的结果或什么?你是什​​么意思? – Quassnoi 2012-04-27 15:58:16

+0

当查询成功时,它会在COUNT(*)'中返回多少条记录? – Quassnoi 2012-04-27 16:07:01

回答

0

我敢肯定,所有250M行并没有在感兴趣的日期范围内发生。

问题是日期检查之间的性质强制进行表扫描,因为您无法知道日期落在哪里。

我建议您将250M行表分为几周,几个月,几个季度或几年,只扫描给定日期范围内的分区。您只需扫描范围内的分区即可。这会有所帮助。

如果你走下分区之路,你需要和MySQL DBA交谈,最好是熟悉分区的人。这不是因为心脏不好。

http://dev.mysql.com/doc/refman/5.1/en/partitioning.html

+0

我是新来的MySQL,并没有很多的理解做分区,我们有一些cronjob当前设置每天做物理创建的表,但这个查询是使用主表,我没有访问权限改变任何应用程序方 – user1361504 2012-04-27 16:09:56

0

添加int_field到索引:

CREATE INDEX idx_backup_jobs_state_status_param_1_updated_at_backup_profile_id ON backup_jobs (state, status_param_1, updated_at, backup_profile_id) 

,使其覆盖所有领域。

通过这种方式,表格查找(您会在计划中看到Using index),这将使您的查询更快(您的里程可能会有所不同)10x

还要注意的是(至少提供的单日期范围)GROUP BY DATE(updated_at)ORDER BY date DESC是多余的,只会使查询使用temporaryfilesort没有任何真正的目的。不过,如果你不能改变查询,那么你可以做很多事情。

+0

谢谢你的建议,我会尝试。我也知道updated_at字段的GROUP BY和ORDER BY子句是多余的,但我有一定的局限性。 – user1361504 2012-04-27 16:27:45

+0

@ user1361504:您是否也可以请回答评论中的问题以回复原来的帖子? – Quassnoi 2012-04-27 16:34:44

0

根据您的查询,您必须在这里领先 - 最小的粒度。我们不知道活动的频率是什么,Field1,Field2状态条目是什么,数据传回的距离多远,在给定的SINGLE DATE上有多少个条目是正常的。所有这一切,我会建立我的索引基于最小粒度首先严格匹配您的查询条件。例如:如果您的“Field1”有十几个可能的“CHAR”值,并且您正在应用“IN”子句,并且Field1在您的索引中处于第一位,它将针对每个日期和field2值的每个字符点击。特别是基于历史,2.5亿条记录可能会强制大量索引分页活动。同样与你的Field2。但是,由于Field2上的“分组依据”条款和更新日期,我将在索引的第一/第二位置分别列出其中一个。根据历史数据,我甚至会倾向于在以下指数上拍摄日期作为主要依据,并在此基础上制定次要标准。

指数(的updated_at,字段2,字段1,INT_FIELD)

这样,你的整个查询可以单独只指数来完成,并不需要对查询的实际记录的原始数据。所有的字段都在索引中。你有一个有限的日期范围,所以你的updated_at是合格的,并且为了准备小组。从这个角度来看,Field2的“CHAR”值将会很好地完成你的团队。 Field1来限定你的第三个标准“IN”字符列表,最后你的INT_FIELD为count(distinct)。

不知道指数需要多长时间才能建立2.5亿,但那是我开始的地方。