2011-09-29 34 views
10

我有以下MySQL表(簡體):爲什麼在MySQL中刪除這個索引加快了我的查詢100x?

CREATE TABLE `track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` varchar(256) NOT NULL, 
    `is_active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `is_active` (`is_active`, `id`) 
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8 

的「IS_ACTIVE」列標誌着我想在最忽略行,但不是全部,我的查詢。我有一些查詢會定期從這張表中讀取塊。其中一個看起來像這樣:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 

該查詢需要一分鐘才能執行。下面是執行計劃:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| 1 | SIMPLE  | t  | ref | PRIMARY,is_active | is_active | 1  | const | 3747543 | Using where | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 

現在,如果我告訴MySQL忽略「IS_ACTIVE人指數,查詢發生瞬間。

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | PRIMARY  | PRIMARY | 4  | NULL | 1597518 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

現在,什麼是真正奇怪的是,如果我強迫MySQL使用了「IS_ACTIVE人指數,查詢再次發生瞬間!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | is_active  |is_active| 5  | NULL | 1866730 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

我只是不明白這種行爲。在'is_active'索引中,行應按is_active排序,後跟id。我在查詢中同時使用了'is_active'和'id'列,所以它似乎應該只需要在樹上做幾跳就可以找到ID,然後使用這些ID從表中檢索標題。

發生了什麼事?

編輯:更多信息關於我在做什麼:

  • 查詢緩存被禁用
  • 運行OPTIMIZE TABLE和ANALYZE TABLE沒有效果
  • 6620372行已 'IS_ACTIVE' 設置爲True。 874,714行將'is_active'設置爲False。
  • 使用FORCE INDEX(is_active)再次加快查詢速度。
  • MySQL版本54年1月5日
+2

您在基準測試之前清除緩存,對吧? – dfb

+0

同時確保表格統計數據是當前的,並且索引被重建。 (然而,這是在MySQL中完成的;-) – 2011-09-29 02:09:47

+0

如果您反轉WHERE條件會發生什麼? 'where(track.id> 5580702 and track.is_active = 1)' – EJP

回答

7

它看起來像MySQL正在做一個糟糕的決定如何使用索引。

從該查詢計劃中可以看出,它可以使用PRIMARY或is_active索引,並且選擇了is_active以便首先通過track.is_active進行縮小。但是,它只使用索引的第一列(track.is_active)。那得到它3747543結果,然後必須被過濾和排序。

如果它已經選擇了PRIMARY索引,它將能夠使用索引將其縮小到1597518行,並且它們將按track.id的順序檢索,這不需要進一步排序。那會更快。

新的信息:

在您使用FORCE INDEX第三種情況下,MySQL的使用只能用第一列,它使用兩列(見key_len)的IS_ACTIVE指數,但現在不是。因此,它現在能夠通過is_active進行縮小,並使用相同的索引通過id進行排序和過濾,並且因爲is_active是單個常量,所以ORDER BY由第二列滿足(即,索引的單個分支中的行已經按排序順序)。這似乎是比使用PRIMARY更好的結果 - 也許你首先想要的是,對吧?

我不知道爲什麼它不使用FORCE INDEX這個索引的兩個列,除非查詢之間以微妙的方式更改。如果沒有,我會把它放到MySQL做出錯誤的決定。

+0

當然,如果你知道比MySQL更好,你總是可以使用[USE INDEX()](http://dev.mysql.com/doc/refman/5.5/en/index-hints.html)來建議它的索引應該更喜歡。你也可以嘗試[ANALYZE TABLE](http://dev.mysql.com/doc/refman/5.5/en/analyze-table.html)給MySQL一個自己弄清楚的機會,有時這可能有效。 – thomasrutter

+0

如果我使用FORCE INDEX(is_active),則查詢立即發生(請參閱最近的編輯)。有任何想法嗎? – cwick

+0

我不確定 - 可能是某種緩存?也許爲此添加EXPLAIN輸出?你是否以相同的順序獲得相同的輸出? – thomasrutter

1

我認爲加速是由於您的WHERE子句。我假設它只是檢索整個大表中的一小部分行。在小型子集上對is_active的檢索數據進行表掃描比對大型索引文件進行篩選要快。遍歷單列索引比遍歷組合索引要快得多。

相關問題