2016-12-29 88 views
1

我試圖優化一個涉及兩個表的左連接,但是我無法讓我的頭繞着可能的索引加速事情。 表1包含2171289行:MySQL左加入分組 - 索引優化

text_metadata_for_nzcorpus | CREATE TABLE `text_metadata_for_nzcorpus` (
    `text_id` varchar(255) NOT NULL, 
    `newspaper` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `year` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `month` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `day` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `section` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `subsection` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `topics` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, 
    `words` int(11) NOT NULL DEFAULT '0', 
    `cqp_begin` bigint(20) unsigned NOT NULL DEFAULT '0', 
    `cqp_end` bigint(20) unsigned NOT NULL DEFAULT '0', 
    PRIMARY KEY (`text_id`), 
    KEY `newspaper` (`newspaper`), 
    KEY `year` (`year`), 
    KEY `month` (`month`), 
    KEY `day` (`day`), 
    KEY `section` (`section`), 
    KEY `subsection` (`subsection`), 
    KEY `topics` (`topics`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

第二個表只包含8584行:

db_dist_fb8ddyk760 | CREATE TABLE `db_dist_fb8ddyk760` (
    `text_id` varchar(255) COLLATE utf8_bin DEFAULT NULL, 
    `beginPosition` int(11) DEFAULT NULL, 
    `endPosition` int(11) DEFAULT NULL, 
    `refnumber` mediumint(9) NOT NULL AUTO_INCREMENT, 
    KEY `refnumber` (`refnumber`), 
    KEY `text_id` (`text_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=16384 DEFAULT CHARSET=utf8 COLLATE=utf8_bin | 

我需要運行以下類型的查詢:

SELECT md.day as handle, count(db.text_id) as hits, 
    count(distinct db.text_id) as files FROM text_metadata_for_nzcorpus as md 
    LEFT JOIN db_dist_fb8ddyk760 as db on md.text_id = db.text_id 
    GROUP BY md.day; 

目前這需要更多處理時間超過5秒。由於這是我在網頁上顯示輸出之前需要運行的很多查詢中的一種,如果可能的話,我希望加快速度。這裏是「解釋」的輸出:

+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref     | rows | Extra     | 
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 
| 1 | SIMPLE  | md | index | day   | day  | 768  | NULL     | 2452080 | Using index    | 
| 1 | SIMPLE  | db | ref | text_id  | text_id | 768  | cqpweb_db.md.text_id |  1 | Using where; Using index | 
+----+-------------+-------+-------+---------------+---------+---------+----------------------+---------+--------------------------+ 

任何有幫助的建議,將不勝感激。 (我不是系統的開發人員,我不負責代碼本身 - 但如果事情可以改進,我想爲程序員提供輸入...)

非常感謝! Sebastian

回答

1

您的EXPLAIN報告顯示您已經在兩個表中使用索引,並且您沒有爲GROUP BY使用臨時表,並且兩個表都使用覆蓋索引(「使用索引」)。

一些其他的事情,你除了可以創建索引做:

  • 定義db_dist_fb8ddyk760.text_id爲NOT NULL。這可能會消除「使用哪裏」筆記,這意味着它必須評估表達式作爲搜索的一部分。這可能會稍微更有效率。
  • 將db_dist_fb8ddyk760.text_id定義爲該表的PRIMARY KEY,如果這樣做合理 - 換句話說,如果text_id在該表中是唯一的。這樣,「type:ref」將變成「type:eq_ref」,這意味着一個獨特的密鑰查找,這更有效一些。但是,如果此表需要爲每個text_id記錄多個匹配,當然會忽略此建議。
  • 將您的innodb_buffer_pool_size增加得足夠多,以便索引可以緩存在內存中。如果查詢只從緩衝池讀取索引頁,則可以獲得更好的性能和更少的磁盤I/O。
  • 利用MySQL Query Cache,所以如果您再次運行相同的查詢,它將重用先前查詢的結果。但是,如果這些表中的數據更改頻率比執行查詢更頻繁,則查詢緩存可能沒什麼用處。
  • 考慮將結果緩存在應用程序內存或memcached或其他東西中。

回覆您的評論:

順便說一句,表db_dist_fb8ddyk760很可能只有一次或兩次,然後丟棄使用。

那你爲什麼要將它存儲在持久數據庫中呢?

考慮使用像Redis一樣的內存中鍵/值存儲。使每個鍵對應一天,並且每個值都是包含點擊次數和不同text_id集合的結構。這基本上是製作一個彙總表(您也可以在SQL中完成),但Redis是內存中的。

+0

感謝您的支持。不幸的是,text_id不能成爲主鍵。將嘗試你建議的其他事情。 –

+0

因爲它被緩存,並且可以在其他用戶執行相同的查詢時再次使用 - 這爲創建這些數據庫節省了相當多的時間。沒有辦法事先了解多久使用一次特定數據庫的用戶數量。有時30個人可能會做同樣的事情(這就是爲什麼緩存有意義),有時用戶可能會導致編譯一個巨大的表僅僅看一次輸出......我們已經選擇了持久數據庫選項,因爲在整體來看,這似乎是最好的折衷方案。 –

+0

另外,「日」不是我認爲你認爲它是... ;-)「日」只是一個句柄,可以包含文本集合中的任何級別的註釋(在這種情況下,它確實是一天的月份,即1到31之間的數字)。所有這些涉及到電子文本語料庫的接口 - http://cwb.sourceforge.net/cqpweb.php - 如果您有興趣的話。 –

2

請勿盲目使用VARCHAR(255)。使用對數據有意義的數據類型。其中許多列聽起來像數字,而不是字符串。

假設年+日+日只是DATE的一部分,請使用數據類型爲DATE的單列。然後,使用DAY(date_col)提取日期。

每個InnoDB表應該有一個PRIMARY KEY。也許組合(text_id, beginPosition)是獨一無二的,可能是PK?

每一列都是NULL ??我對此表示懷疑。讓他們NOT NULL除非你有一個NULL的原因。

refnumberAUTO_INCREMENT,但不是PRIMARY KEY?是什麼賦予了?

進行上述更改將有助於某些。但是,所述的查詢註定要掃描整個2M行表並進入另一個表。事情可以完成。但是他們將涉及構建和維護摘要表。

+0

完全同意有一個彙總表...即使它是預先彙總在一個特定的一天結束時,然後它只完成一次,他們可以聯合只爲條目最新的一天。 – DRapp

+0

感謝你 - 一些評論:我理解你對數字而不是VARCHAR所說的話 - 但該表是一個需要靈活的系統的一部分。從一開始就不清楚在各個欄目中找到了哪些類型的數據。是的,(text_id,beginPosition)的組合是唯一的 - 將研究這個問題,還有關於列爲NULL的問題。順便說一句,表db_dist_fb8ddyk760很可能只能使用一次或兩次,然後丟棄。所以我正在尋找第一次工作的優化... –

+0

另一個問題是...「日」是每月的哪一天?或者是其他東西? (我想知道分組的目的是什麼。) –