2014-05-25 132 views
1

我已經閱讀了幾個關於此問題的其他堆棧溢出問題,但它仍然沒有意義。MySQL沒有使用索引來排序

我與sakila的世界測試數據庫進行試驗,這是我的表定義:

CREATE TABLE `City` (
    `ID` int(11) NOT NULL AUTO_INCREMENT, 
    `Name` char(35) NOT NULL DEFAULT '', 
    `CountryCode` char(3) NOT NULL DEFAULT '', 
    `District` char(20) NOT NULL DEFAULT '', 
    `Population` int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`ID`), 
    KEY `CountryCode` (`CountryCode`), 
    KEY `city_name` (`Name`), 
    CONSTRAINT `city_ibfk_1` FOREIGN KEY (`CountryCode`) REFERENCES `Country` (`Code`) 
) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=latin1 

這裏是我的指標:

mysql> show index from City; 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| City |   0 | PRIMARY  |   1 | ID   | A   |  4188 |  NULL | NULL |  | BTREE  |   |    | 
| City |   1 | CountryCode |   1 | CountryCode | A   |   465 |  NULL | NULL |  | BTREE  |   |    | 
| City |   1 | city_name |   1 | Name  | A   |  4188 |  NULL | NULL |  | BTREE  |   |    | 
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 

我試圖理解爲什麼MySQL是不使用這裏對結果進行排序的索引:

mysql> explain select * from City order by Name asc; 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| 1 | SIMPLE  | City | ALL | NULL   | NULL | NULL | NULL | 4188 | Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 

我不明白爲什麼MySQL在這個文件夾中做文件情況下,很明顯城市名稱上的索引已經排序。

我看了一些其他的問題,人們在他們的索引中使用前綴,這是禁止MySQL使用該索引進行排序。當我在名稱上創建該索引時,我沒有使用前綴。

其他人也期待在Extra列中看到「使用索引」。我的理解是,這意味着索引'覆蓋'了查詢,這意味着實際的表不需要被讀取,因爲索引具有所有的值。所以我不希望在額外列中看到這一點,因爲索引僅在名稱上,還有其他列。

我覺得這個術語「使用索引」有點讓人誤解,MySQL可以使用索引來過濾結果,但仍然需要讀取表格。在這種情況下,「使用索引」不會成爲Extra列的一部分。太誤導了。

有人請向我解釋爲什麼MySQL仍然在使用該查詢的文件?如果你想知道的話,有4079行。

另外,是否有任何明確的方法知道MySQL使用索引來排序結果?

+0

優化器看到您將讀取表中的所有記錄,因此它傾向於從PRIMARY索引讀取而不是從輔助city_name讀取。因此MySQL必須做額外的排序。 – akuzminsky

回答

0

這是您的查詢:

select * 
from City 
order by Name asc; 

有兩個部分來此查詢。一部分是以正確的順序獲取名稱值。另一部分是獲取所有其他列。 MySQL必須比較這兩個操作的成本。

查詢有兩種可能的路徑。首先是採取所有列並按名稱排序。然後只是返回結果。這是filesort方法。第二個是按順序讀取索引,然後在數據表中逐一查找行。

MySQL已經決定第一種方法更快。如果你只有一行,這顯然是真的(爲什麼只讀一行索引)?我的猜測是你在表格中的數據非常少。隨着您添加更多數據,指數的使用將會更加有益。

作爲一個說明,這個查詢通常應該使用索引方法:

select Name 
from City 
order by Name asc; 
+0

MySQL需要多少行才能決定使用第二種方法更好?我已經有超過4000行的表格。所以你在說排序4000行花費的時間少於從索引中獲取行的時間。 – msknapp

+0

考慮使用某種優化的排序對任何一組4000個密鑰進行排序。時間可以忽略不計。從磁盤傳輸更爲昂貴(考慮您需要同時傳輸索引結構和錶行)。然而,沒有固定的數字,這一切都取決於基於統計和設計的啓發式。 4000行是絕對微小的行數,所有事情都考慮在內。 – SystemFun

+0

@msknapp。 。 。 4,000似乎足以讓MySQL使用索引。問題:爲什麼你使用'char()'值而不是'varchar()'?固定長度記錄較大,可能會拋棄優化決策。 –

0

通常,當你不決定過濾結果(即沒有where子句)時,RDBMS將決定使用排序/過濾器的方法(而不是索引),因爲你上面提到的原因。您試圖返回與表格中所有行相關的所有信息,只是爲了返回索引列以外的數據而無需使用索引就可以更高效地完成查找和傳輸操作一旦在索引中找到密鑰,即可對該表進行操作。

換句話說,索引建立在您選擇的字段上,但不包含任何有關該表的其他相關信息......因此,它必須引用回表的真實位置檢索你請求的附加元數據,這種簡單的排序記錄效率較低(假設你有這麼小的數據集)。這是因爲在您的(名稱)列上對一個小數據集進行排序比根據索引檢索值更快,並且將它們排序。

然而,從我提到的確切原因,從任意大的表中檢索大容量的寬記錄時,通常不會使用索引。你可以給出計劃者的提示,這將強制它使用索引來驗證我在這裏提到的內容......你也可以通過一些大的因素來增加你的數據集,然後嘗試選擇一小部分來測試我的理論。