2013-10-05 64 views
4

我需要幫助來優化此查詢。在連接查詢中優化按子句的順序

SELECT messages.* 
    FROM messages 
    INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

沒有限制,該查詢返回200K行,大約需要1.3-2秒才能運行。這個問題似乎是按照從句的順序。沒有它,查詢需要.0005秒。

Indexes: 
    (subscription.user_id, subscription.entity_id) 
    (subscription.entity_id) 
    (messages.timestamp) 
    (messages.entity_id, messages.timestamp) 

我能夠通過改變查詢此以提高性能:

SELECT messages.* FROM messages 
INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
INNER JOIN ( 
    SELECT message_id FROM messages ORDER BY timestamp DESC 
) as temp on temp.messsage_id = messages.message_id 
WHERE subscription.user_id = 1 LIMIT 50 

這個運行在0.12秒。一個非常好的改進,但我想知道它是否會更好。它似乎 如果我能以某種方式篩選第二個內部聯接,那麼事情會更快。

謝謝。

架構:

messages 
     message_id, entity_id, message, timestamp 

    subscription 
     user_id, entity_id 

UPDATE

雷蒙德Nijland的回答解決了我最初的問題,但另一隻冒出了

SELECT messages.* 
    FROM messages 
    STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

直連接是在兩種情況下效率低:

  1. 沒有在認購表中沒有的user_id進入

  2. 有郵件表

關於如何解決此問題的任何建議一些相關的條目?如果不是從查詢的角度來看,應用程序呢?

UPDATE

EXPLAIN信息

LIMIT 50

| id | select_type | table    | type | possible_keys       | key   | key_len | ref         | rows | Extra  | 
| 1 | SIMPLE  | messages   | index | idx_timestamp       | idx_timestamp | 4  | NULL         | 50 |    | 
| 1 | SIMPLE  | subscription  | eq_ref | PRIMARY,entity_id,user_id    | PRIMARY  | 16  | const, messages.entity_id    | 1 | Using index | 

無極限

| id | select_type | table    | type | possible_keys       | key   | key_len | ref         | rows | Extra   | 
| 1 | SIMPLE  | messages   | ALL | entity_id_2,entity_id     | NULL   | NULL | NUL         | 255069 | Using filesort| 
| 1 | SIMPLE  | subscription  | eq_ref | PRIMARY,entity_id,user_id    | PRIMARY  | 16  | const, messages.entity_id    |  1 | Using index | 

CREATE TABLE語句:

隨着〜5000行

subscription | CREATE TABLE `subscription` (
    `user_id` bigint(20) unsigned NOT NULL, 
    `entity_id` bigint(20) unsigned NOT NULL, 
    PRIMARY KEY (`user_id`,`entity_id`), 
    KEY `entity_id` (`entity_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

與〜255,000行

messages | CREATE TABLE `messages` (
    `message_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `entity_id` bigint(20) unsigned NOT NULL, 
    `message` varchar(255) NOT NULL DEFAULT '', 
    `timestamp` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`message_id`), 
    KEY `entity_id` (`entity_id`,`timestamp`), 
    KEY `idx_timestamp` (`timestamp`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
+1

您可以發佈展會CREATE TABLE語句? –

+0

單個用戶200,000行?你確定嗎? –

+0

@DanBracuk是的,我相信 –

回答

3

刪除索引messages.entity_id這個人是多餘的,嘗試一個STRAIGHT_JOIN我認爲MySQL優化是accesing錯了你的表訂購。 MySQL需要首先訪問表消息,以便它可以使用郵件上的索引(entity_id,timestamp),並刪除「使用臨時文件;使用文件夾」的需要(如果MySQL需要創建基於MyISAM磁盤的表並且需要用磁盤I/O讀取和I/O寫入對此進行排序(快速排序)。

SELECT STRAIGHT_JOIN messages.* 
    FROM messages 
    INNER JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

OR

SELECT messages.* 
    FROM messages 
    STRAIGHT_JOIN subscription ON subscription.entity_id = messages.entity_id 
    WHERE subscription.user_id = 1 
    ORDER BY messages.timestamp DESC 
    LIMIT 50 

我也有這個問題,我固定它像這樣http://sqlfiddle.com/#!2/b34870/1但隨後國家/地區表

編輯,因爲關在STRAIGHT_JOIN賈森中號反應

直接連接在兩種情況下效率低下:

沒有在認購表

沒有USER_ID進入事實上的MySQL優化與INNER JOIN會觸發一個「不可能WHERE閱讀常數表後發現」,也不會執行查詢。 但STRAIGHT_JOIN不會觸發「讀取常量表後不可能發現的WHERE問題」,因此需要完成索引掃描(可能是完整的),以查找其user_id值,這可能會減慢查詢的執行速度。 簡易的解決將是:利用現有的USER_ID的與STRAIGHT_JOIN

有郵件表一些相關條目

可能同樣的問題在這裏的MySQL認爲它應該做的(也許全)索引掃描到找到結果。但我需要看到EXPLAIN語句知道肯定

你也可以試試這個查詢第一

SELECT 
* 
FROM (

SELECT 
    entity_id 

FROM 
    subscriptions 

WHERE 
    subscription.user_id = 1 
) 
subscriptions 

INNER JOIN 
messages 

ON 
subscriptions.entity_id = messages.entity_id 

ORDER BY 
messages.timestamp DESC 

LIMIT 50 
+1

Filesort不一定很慢。而且是一個用詞不當,它不**意味着通過磁盤文件執行! –

+0

我知道......我的意思是「使用臨時;使用filesort」組合可能導致使用快速排序算法和大量磁盤IO對基於MyISAM磁盤的臨時表進行排序 –

+0

感謝Raymond,這絕對解決了我的問題。查詢現在運行在.000x秒 –