2012-12-15 45 views
3

我有一個簡單的邀請表:MySQL的 - 或者運營商不使用索引

CREATE TABLE `invitation` (
    `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `inviter_id` int(10) unsigned NOT NULL, 
    `invitee_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`invitation_id`), 
    UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`) 
) 

我想選擇由邀請方70的邀請,被邀請者62,反之亦然:

EXPLAIN SELECT * FROM `invitation` WHERE 
(invitee_id = 70 AND inviter_id = 62) OR (invitee_id = 62 AND inviter_id = 70) 

但此查詢是ALL類型,不使用invitee_inviter_idx。 請告訴我這裏有什麼問題?

謝謝!

==編輯== 對不起,我錯了模式,它有一個字段:request_ts。這次查詢計劃是ALL。

CREATE TABLE `invitation` (
     `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
     `inviter_id` int(10) unsigned NOT NULL, 
     `invitee_id` int(10) unsigned NOT NULL, 
     `request_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
     PRIMARY KEY (`invitation_id`), 
     UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`) 
    ) 

這裏是我的應歸結爲結果:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE invitation ALL invitee_inviter_idx \N \N  \N 1 Using where 
+2

表中有多少條記錄?如果表格非常小​​,那麼查詢優化器可能會決定進行全面掃描,因爲這樣做非常便宜。 – Hammerite

+0

我想你只是在某些情況下才能得到它的MyISAM表;即當您在「額外」列中看到「不可能在讀取常量表後注意到的地方」時,發現 – a1ex07

+0

@Salman A,對不起,我錯了,我更新了模式。 – robinmag

回答

2

你只需要在表格中得到足夠的行。 MySQL會對小桌面進行全表掃描,因爲它足夠便宜。

我的示例將65k行放入表中,它將使用索引。

http://sqlfiddle.com/#!2/63079/1

+1

足夠的行,它停止掃描表並進行索引的部分掃描 - 一個改進,但invitee_id的退化值將掃描整個索引。聯盟的所有結果都是const類型的,而不是範圍 –

+1

我同意聯盟會更快,但這不是OP要求的。 –

4

有,爲什麼你選擇不使用你使用的select *,其中包括項目不是索引

1)至少3個原因在指數中(即invitation_id)。這意味着它使用索引後,它將不得不查找數據庫中的行來獲取invitation_id值。如果您將invitation_id添加到索引,它會使用該索引。如果你已經完成了select只是invitee_id, inviter_id,它會使用索引。

2)查詢優化器決定只掃描表而不是掃描索引範圍會更好。當優化器嘗試確定全表掃描或部分索引掃描時,它不會爲您的確切查詢做它 - 它需要一個通用的良好計劃。一個可能會被重複運行。從invitee_id,inviter_id(70,62)的掃描可能只有8個索引條目,但是如果從50k條目中隨機選取,則平均距離約爲17k條目。因此,平均來說,一個查詢將訪問索引的1/3(即將其拉入內存),然後訪問該行所在的頁面(請參閱#1),將其拖入內存。你的行很小,訪問只有一個項目可能會拉動680行(8K頁12字節的3 32位#),這是表的1/70 - 做100個查詢,可能你已經把整個索引拉入內存和整個表格 - 通過掃描表格並花費40%的內存來存放其他表格的位數更有意義。在某個時候(這似乎是65k行)停止了合理的處理。

3)你的問題說的是:你使用了一個OR。 OR表達式不能用於在索引中查找某些內容 - 也就是說,無法查找62或70.相反,它會生成一個查找範圍(62,70),然後掃描到(70,62)(請參閱#2爲什麼這可能很糟糕)。

您問「這裏有什麼問題」 - 這是您使用的OR,它不會縮放。您不僅需要避免所有類型,還需要避免使用大類型的RANGES。

我見過與其他SQL引擎相同的問題,我使用的解決方案是UNION ALL。

喜歡的東西

SELECT * FROM `invitation` WHERE 
    (invitee_id = 70 AND inviter_id = 62) 
UNION ALL 
SELECT * FROM `invitation` WHERE 
    (invitee_id = 62 AND inviter_id = 70) 

這將使做兩個查詢,並沒有檢查重複合併的結果。

這對內存使用來說要輕很多,而且要快得多 - 只需索引的幾頁和表中的兩頁以及每個查找的O(log(N))。這是因爲它現在是const類型的 - 你的目標是消除ALL,但切換到RANGE幾乎與只讀取兩行相同。由於O(1/3 * N)爲O(N),所以掃描整個表格爲O(N)並且掃描索引的RANGE也是O(N)。換句話說,它不會縮放。

+0

感謝您的回答,投票贊成:) – robinmag