2016-04-29 36 views
2

我在AWS m4.large(2個vCPU,8 GB RAM)上運行,我看到有關MySQL和GROUPBY的稍微令人驚訝的行爲。我有這樣的測試數據庫:`MySQL GROUP BY在使用索引時速度較慢

CREATE TABLE demo (
    time INT, 
    word VARCHAR(30), 
    count INT 
); 
CREATE INDEX timeword_idx ON demo(time, word); 

我插入400萬條記錄與(均勻)隨機單詞"t%s" % random.randint(0, 30000)和時間random.randint(0, 86400)

SELECT word, time, sum(count) FROM demo GROUP BY time, word; 
3996922 rows in set (1 min 28.29 sec) 

EXPLAIN SELECT word, time, sum(count) FROM demo GROUP BY time, word; 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+ 
| id | select_type | table | type | possible_keys | key   | key_len | ref | rows | Extra | 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+ 
| 1 | SIMPLE  | demo | index | NULL   | timeword_idx | 38  | NULL | 4002267 |  | 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------+ 

,然後我不使用索引:

SELECT word, time, sum(count) FROM demo IGNORE INDEX (timeword_idx) GROUP BY time, word; 
3996922 rows in set (34.75 sec) 

EXPLAIN SELECT word, time, sum(count) FROM demo IGNORE INDEX (timeword_idx) GROUP BY time, word; 
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra       | 
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+ 
| 1 | SIMPLE  | demo | ALL | NULL   | NULL | NULL | NULL | 4002267 | Using temporary; Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+---------+---------------------------------+ 

你可以通過查詢所花費的3倍多的時間指數看。我沒有那麼驚訝,因爲通過使用索引查詢可能必須避免閱讀timeword列,但不幸的是索引非常稀疏,它不應該獲得太多。相反,當檢索count時,它將直接掃描轉換爲隨機訪問模式。

我只想確認這是原因,並想知道是否有一個「緊湊的規則」時和索引會最終導致更糟糕的性能時使用GROUP BY。

編輯:

我跟着戈登·利諾夫答案,並用:

SELECT word, time, sum(count) FROM demo GROUP BY time, word; 
3996922 rows in set (3.36 sec) 

EXPLAIN SELECT word, time, sum(count) FROM demo GROUP BY time, word; 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key   | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | demo | index | NULL   | timeword_idx | 43  | NULL | 4002267 | Using index | 
+----+-------------+-------+-------+---------------+--------------+---------+------+---------+-------------+ 

CREATE INDEX timeword_idx ON demo(time, word, count); 

的 「覆蓋索引」 當與全掃描相比,計算出結果快10倍非常令人印象深刻!

回答

3

你有一個合理的大小的表,所以問題可能是順序訪問數據或顛簸。使用索引需要檢索索引,然後查找數據頁面中的數據以獲取count

這實際上可能比只讀頁面和排序更糟糕,因爲頁面沒有按順序讀取。連續讀取比隨機讀取更優化。在最壞的情況下,頁面緩存已滿,隨機讀取需要刷新頁面。如果發生這種情況,可能需要多次讀取單個頁面。只有400萬個相對較小的行,除非你受到嚴重的內存限制,否則顛簸是不太可能的。

如果這種解釋是正確的,那麼包括在指數count應加快查詢:

CREATE INDEX timeword_idx ON demo(time, word, count); 
+1

關於使用索引的「緊湊規則」的另一部分是關於限制需要訪問的行數的謂詞(條件),以及MySQL是否可以有效使用索引範圍掃描操作。如果必須訪問表中的每個*行,並且查詢不使用「覆蓋」索引,則需要查找基礎表中的頁面。這就像訪問索引中的每個*塊一次,並且多次訪問表中的每個*塊。如果這是InnoDB表,沒有主鍵或唯一索引,則集羣鍵是內部rowID。 +10 – spencer7593

+0

「覆蓋指數」給出了驚人的結果。更新了問題以顯示它們。 – neverlastn

1

從手冊頁How MySQL Uses Indexes

指標不太重要,查詢小表,或大表 報告查詢處理大部分或全部行。當查詢 需要訪問大多數行時,按順序讀取比通過索引工作的 快。順序讀取最小化磁盤搜索,即使不是查詢所需的所有行,也可以使用 。

至於增加更多列以創建覆蓋索引(其中沒有訪問數據頁但所有數據在索引中可用的索引),請小心。他們需要付出代價。在你的情況下,你的索引無論如何都會變寬。但總是需要仔細的平衡。

正如斯賓塞所暗示的,基數總是與範圍起作用。有關基數信息,請使用show index from tblName命令。這不是您查詢的動力問題,但在其他設置中很有用。我應該改述一下:桌子的基數非常高。所以你的索引在查詢中被認爲是它的障礙。

+1

給出30,000個單詞值和86,400個時間值...這是2,592,000,000個可能的元組。由於分佈均勻,只有4,000,000行,所以重複的可能性很小。我們希望對數據頁面進行全面掃描,排序操作將會快得多。通過查詢的覆蓋索引(以word和time作爲前導列),GROUP BY操作將通過使用索引來優化,以避免排序。將INT列添加到索引只會爲每個索引條目添加4個字節。 +10 – spencer7593

+1

我不是在爭辯。它已經很廣了。應該注意不要把每個索引變成一個初級開發人員的覆蓋範圍。 「覆蓋指數」的 – Drew

+0

給出了驚人的結果。更新了問題以顯示它們。 – neverlastn

相關問題