2009-12-23 56 views
6

我有一個表有一個存儲一些blob數據的表的外鍵。當我在主表上使用條件對錶進行內部連接時,連接類型從'索引'變爲'ALL'。我想避免這種情況,因爲我的blob表的數量級爲幾十GB。我怎樣才能避免它?如何避免使用此基本內連​​接進行全表掃描?

這裏是基本內加入:

EXPLAIN SELECT m.id, b.id, b.data 
     FROM metadata m, blobstore b 
     WHERE m.fkBlob = b.id; 

1, 'SIMPLE', 'm', 'index', 'fk_blob', 'fk_blob', '4', '', 1, 'Using index' 
1, 'SIMPLE', 'b', 'eq_ref', 'PRIMARY', 'PRIMARY', '4', 'blob_index.m.fkBlob', 1, '' 

在這裏,我添加一個條件在主臺:

EXPLAIN SELECT m.id, b.id, b.data 
     FROM metadata m, blobstore b 
     WHERE m.fkBlob = b.id AND m.start < '2009-01-01'; 
1, 'SIMPLE', 'b', 'ALL', 'PRIMARY', '', '', '', 1, '' 
1, 'SIMPLE', 'm', 'ref', 'fk_blob,index_start', 'fk_blob', '4', 'blob_index.b.id', 1, 'Using where' 

注意,在其中的表中列出的順序發生了變化。由於我在主表中添加了一個條件,現在它正在對blob表進行全表掃描。

這裏是架構:

DROP TABLE IF EXISTS `blob_index`.`metadata`; 
    CREATE TABLE `blob_index`.`metadata` (
     `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
     `fkBlob` int(10) unsigned NOT NULL, 
     `start` datetime NOT NULL, 
     PRIMARY KEY (`id`), 
     KEY `fk_blob` (`fkBlob`), 
     KEY `index_start` (`start`), 
     CONSTRAINT `fk_blob` FOREIGN KEY (`fkBlob`) REFERENCES `blobstore` (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 


    DROP TABLE IF EXISTS `blob_index`.`blobstore`; 
    CREATE TABLE `blob_index`.`blobstore` (
     `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
     `data` mediumblob NOT NULL, 
     PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

回答

3

我猜你是對空表想這(因爲MySQL認爲它需要經過一個連續做全表掃描),什麼可能會影響調度的結果。當你在真正的桌面上進行時,EXPLAIN的結果可能會有所不同(實際上在我的測試中有所不同)。

+0

您的權利,我扔了一些數據,在這兩個表上運行分析表,現在第一個連接是元表上的類型範圍。 – Fredrick 2009-12-30 17:51:02

0

如果我看了你發佈的內容,它從indexrefeq_refall

CREATE INDEX idx_metadata USING BTREE ON `metadata` (fkBlob,start); 

應該馬上回來。

3

優化器認爲您的查詢將受益於交換表順序(這很可能意味着統計信息不是最新的)。

您可以嘗試在metadata (start, fkBlob)添加索引:

CREATE INDEX ix_metadata_start_blob ON metadata (start, fkBlob) 

並運行兩個表ANALYZE TABLE

這樣,start上的索引將用於過濾metadata,這將成爲領先。

您也可以明確強制的加入順序:

SELECT * 
FROM metadata m 
STRAIGHT_JOIN 
     blobstore b 
ON  b.id = m.fkBlob 
WHERE m.start <= '2009-01-01' 

,但它通常不推薦。

0
if the index doesnot take it right use HINTS 

select /* INDEX <index_name> */ 
blah blah blah 
from ........ 
0

在第一個示例中,MySQL使用元數據fk_blob索引,因爲它是覆蓋索引 - 您在查詢中使用的每個列都存在於索引中。 (這是「使用索引」的意思。)該查詢仍然進行了全面掃描,但它通過輔助索引而不是主要索引掃描每一行。只要你使用start,你就失去了覆蓋索引,MySQL計算出它更快地使用blobstore作爲驅動索引。 (InnoDB的主索引與行存儲集成在一起)。

如果您希望MySQL繼續使用元數據索引作爲驅動索引,請確保其上有一個對查詢有用的索引。 (start,fkBlob)上的索引對於第二個查詢來說是最好的,但對其他查詢可能沒有用處。下一個最好的索引是用(fkBlob,start)替換(fkBlob)。您必須平衡具有太多索引(維護費用昂貴)與具有高效的查詢計劃。測試,測試,測試 - 並且永遠不要盲目相信解釋你的開發數據庫。