1

時,MySQL查詢變得非常緩慢我有一個表,有1500萬行。使用Order By

下面的查詢返回約5億美元(但在15行有限公司)在不到1秒的記錄:

SELECT messages.* FROM messages 
INNER JOIN gateways ON 
messages.gateway_id=gateways.id 
INNER JOIN orders ON 
gateways.order_id=orders.id 
WHERE orders.user_id=6500 AND messages.deleted=0 
AND messages.type='Out' LIMIT 15; 

但是,當我加入Order By ID DESC到它的結束,它變成約極其緩慢〜40秒:

SELECT messages.* FROM messages 
INNER JOIN gateways ON 
messages.gateway_id=gateways.id 
INNER JOIN orders ON 
gateways.order_id=orders.id 
WHERE orders.user_id=6500 AND messages.deleted=0 
AND messages.type='Out' ORDER BY messages.id DESC LIMIT 15; 

任何幫助將不勝感激。

+0

您是否已經在需要的列中使用DESC順序的索引? – winter

+0

是的,我通過'在主鍵列上命令'(id) –

+0

你得到了多少行(每個用戶有多少行)?你可以添加解釋計劃嗎? ''by''使用'id'上的索引很可能不是一個好選擇,但MySQL可能會使用它。你可能需要一個子查詢。 – Solarflare

回答

1

模式SELECT lots_of_stuff ORDER BY something LIMIT small_integer是臭名昭着的造成性能問題。離開ORDER BY something會使性能問題消失。爲什麼?因爲使用ORDER BY的模式會導致MySQL服務器對很大數量的相當大的行進行排序(在您的情況下爲500萬行),但僅放棄其中的少數幾行。這在服務器中使用了大量的RAM,CPU和IO,只是爲了放棄大部分工作。

最好的辦法是在這裏使用延遲連接類型的模式,其中除了message.id值之外什麼都沒有排序。使用這個子查詢來做到這一點。

    SELECT messages.id 
        FROM messages 
       INNER JOIN gateways ON messages.gateway_id=gateways.id 
       INNER JOIN orders ON gateways.order_id=orders.id 
        WHERE orders.user_id=6500 
         AND messages.deleted=0 
         AND messages.type='Out' 
       ORDER BY messages.id DESC 
        LIMIT 15 

這會給你一個很好的收集15 message.id值。

您的下一步是要優化此子查詢。我建議您在messages表格上嘗試覆蓋索引的化合物,其中包含(deleted, type, id, gateway_id)列。這應該有助於加速它。

您也可能需要其他表上的索引。你應該考慮在MySQL中使用EXPLAIN函數來分析你的性能。

最後,使用messages.id值的小集合來獲取所需的messages行,如下所示。 (這是推遲加入;你推遲提取整行,直到你知道你需要這樣,你不必ORDER全亂了哪些行)

編輯添加複合索引在gateways (order_id, id)上,以避免對該表進行全表掃描。這並不是很大,但這可能會有所幫助。

SELECT a.* 
    FROM messages a 
    JOIN (
        SELECT messages.id 
        FROM messages 
       INNER JOIN gateways ON messages.gateway_id=gateways.id 
       INNER JOIN orders ON gateways.order_id=orders.id 
        WHERE orders.user_id=6500 
         AND messages.deleted=0 
         AND messages.type='Out' 
       ORDER BY messages.id DESC 
        LIMIT 15 
     ) b ON a.id = b.id 
ORDER BY a.id DESC 
+1

爲了其他人試圖優化的東西,請考慮發佈一個評論說這對你有效。 –

+0

哇哇!查詢時間變成〜7秒!你太棒了 –

+0

我已經有'(deleted,type,gateway_id)'的複合索引。我應該爲此添加「id」列嗎? –

1

我假定

  • 每個訂單屬於一個用戶
  • 到一個順序

因此

  • 每個網關所屬這樣:

    INNER JOIN gateways ON messages.gateway_id=gateways.id 
    INNER JOIN orders ON gateways.order_id=orders.id 
    WHERE orders.user_id=6500 AND messages.deleted=0 
    

    可以是改寫成英語h爲:

    「獲取屬於屬於該用戶的訂單的網關」。

    現在,要獲取與此用戶相關的最新消息,問題是我們可能會有許多不同的gateway_id(根據您的EXPLAIN約143),所以我們不能使用索引跳過排序。

    好吧,我們可以像奧瓊斯證明的那樣,但是有一個問題。下面是該查詢的簡化版本:

    SELECT ... FROM messages 
    WHERE gateway_id IN (1,2) ORDER BY id DESC LIMIT 10 
    

    如果我們對(ID,gateway_id)索引那麼MySQL將最有可能決定對其進行掃描降序排列。如果它很快找到10條消息「gateway_id IN(1,2)」,那麼它會很快。但是,如果這些gateway_id具有非常舊的消息或根本沒有消息,則可能需要掃描整個索引。

    如果PK關係如我所描述的那樣,我會在消息表中實現一個user_id列,然後它將允許(user_id,message_id)上的索引,這會使查詢計時在毫秒級以下。