2012-04-27 187 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億,但那是我開始的地方。