2011-04-06 52 views
3

我需要一些幫助來解決這個問題。我試圖讓Mysql在DATETIME字段上使用索引。當表有其他字段時,Mysql不使用DATETIME索引

如果表中有其他(未使用的)字段,Mysql決定不使用索引。考慮兩種情況如下:

有2場的簡單表工作正常

DROP TABLE IF EXISTS datetime_index_test; 
CREATE TABLE datetime_index_test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT , 
created DATETIME NOT NULL , 
PRIMARY KEY (id) , 
INDEX (created) 
) ENGINE = InnoDB ; 

INSERT INTO datetime_index_test (created) VALUES 
('2011-04-06 00:00:00'), 
('2011-04-06 01:00:00'), 
('2011-04-06 02:00:00'), 
('2011-04-06 03:00:00'), 
('2011-04-06 04:00:00'), 
('2011-04-06 05:00:00'), 
('2011-04-06 06:00:00'), 
('2011-04-06 00:00:00'); 

EXPLAIN SELECT * FROM datetime_index_test 
WHERE created <= '2011-04-06 04:00:00'; 

+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table    | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | datetime_index_test | range | created  | created | 4  | NULL | 4 | Using where; Using index | 
+----+-------------+---------------------+-------+---------------+---------+---------+------+------+--------------------------+ 

3場一個簡單的表,不正常工作

DROP TABLE IF EXISTS datetime_index_test; 
CREATE TABLE datetime_index_test (
id INT UNSIGNED NOT NULL AUTO_INCREMENT , 
created DATETIME NOT NULL , 
user int(10) unsigned DEFAULT 0, 
PRIMARY KEY (id) , 
INDEX (created) 
) ENGINE = InnoDB ; 

INSERT INTO datetime_index_test (created) VALUES 
('2011-04-06 00:00:00'), 
('2011-04-06 01:00:00'), 
('2011-04-06 02:00:00'), 
('2011-04-06 03:00:00'), 
('2011-04-06 04:00:00'), 
('2011-04-06 05:00:00'), 
('2011-04-06 06:00:00'), 
('2011-04-06 00:00:00'); 

EXPLAIN SELECT * FROM datetime_index_test 
WHERE created <= '2011-04-06 04:00:00'; 

+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+ 
| id | select_type | table    | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+ 
| 1 | SIMPLE  | datetime_index_test | ALL | created  | NULL | NULL | NULL | 8 | Using where | 
+----+-------------+---------------------+------+---------------+------+---------+------+------+-------------+ 

最後, 我的問題; 任何人都可以向我解釋爲什麼Mysql決定不使用索引?

+2

掃描僅包含10條左右記錄的索引是浪費時間,因爲全表掃描需要大約相同的時間,所以mysql只適用於全表掃描。嘗試添加幾千條記錄,看看事情是否改變。 – 2011-04-06 15:37:09

+0

@Mark謝謝你的建議。我嘗試添加20000條記錄並做了ANALYZE TABLE。 EXPLAIN仍然給了我相同的結果,並且搜索了20008行(選擇類型仍然是ALL)。 – Joernsn 2011-04-07 06:28:30

回答

4

這是由於我稱之爲基於關鍵人口(元組基數)的5%規則。

如果您爲存在不平衡基數的表編制索引,那麼MySQL查詢優化器將總是選擇阻力最小的路徑。

例如:如果某個表中有一欄性別,基數是二,M和F.

什麼是你等指標性別欄???你肯定會得到兩個巨大的鏈表。

如果加載一個百萬行與性別列的表,你可能會得到50%M和50%F.

查詢優化過程中的索引變得毫無用處,如果按組合鍵的基數(關鍵就像我說過的那樣)是超過總數的5%。

現在,關於你的例子,爲什麼兩個不同的EXPLAIN計劃?我的猜測是MySQL Query Optimizer和InnoDB作爲標籤團隊。

在第一個CREATE TABLE中,表和索引大小雖然相差不大,所以它通過索引掃描而不是全表掃描來決定贊成索引。請記住,非唯一索引會在其索引條目中攜帶每行的內部主鍵(RowID),從而使索引幾乎與表本身的大小相同。

在第二個CREATE TABLE中,由於引入了另一列,用戶,您現在可以使查詢優化器看到一個完全不同的場景:該表現在更大,索引。因此,查詢優化器在解釋如何使用可用索引方面變得更爲嚴格。它符合我之前提到的5%的規則。該規則失敗了,查詢優化器決定採用全表掃描。