2017-01-03 111 views
2

我有一個表user_notifications具有1100000條記錄,我必須運行下面的查詢,但它需要超過3分鐘完成查詢我可以做些什麼來提高獲取時間。如何使這個SQL查詢更快?

SELECT `user_notifications`.`user_id` 
FROM `user_notifications` 
WHERE `user_notifications`.`notification_template_id` = 175 
AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day)) 
AND `user_notifications`.`user_id` IN (
    1203, 1282, 1499, 2244, 2575, 2697, 2828, 2900, 3085, 3989, 
    5264, 5314, 5368, 5452, 5603, 6133, 6498.. 
) 

用戶ID在IN塊有時可達1k。

優化我已經索引user_idnotification_template_id列在user_notification表中。

enter image description here

+1

比較1000多個記錄中的'user_id'值需要一些時間,即使MySQL正在使用索引。 「年齡」有多長? –

+0

@TimBiegeleisen 2-5分鐘平均值 – Prem

+0

您是否在user_id和notification_template_id上​​創建了單獨的索引,或者兩者都有?試試後者。 – wumpz

回答

7

Big IN()列表本質上很慢。使用索引創建一個臨時表並將IN()列表中的值放入該臨時表中,然後您將獲得索引連接的強大功能,而不是巨大的IN()列表。

+0

謝謝@丹這似乎合乎邏輯。你能告訴我有什麼我們可以爲sent_at字段做什麼,我認爲這也造成了查詢延遲。 – Prem

+0

@Prem你有什麼事實來支持這個觀點嗎? codeforester解決它,我對這個答案的評論也是相關的。但是你的IN子句幾乎肯定是造成大部分延遲的原因。 –

+0

我在'IN()'中看到70K項目的問題,但沒有看到1K的問題。 –

2

你似乎在查詢一個小的日期範圍。有一個基於SENT_AT列的索引怎麼樣?你知道當前查詢使用了什麼索引嗎?

+2

也在那個主題上,你真的必須將'sent_at'轉換爲DATE嗎?看起來像擺脫那個演員陣容會產生相同的結果,因爲如果DATE(sent_at)大於給定值,那麼sent_at本身必須至少是那麼大。 –

+2

@DanFarrell不是100%肯定的,但我敢打賭1000個用戶ID比較就是殺死這個查詢。 –

+1

DATE(sent_at)可能有問題,因爲它可能會阻止因爲函數調用而使用任何基於sent_at的索引,除非您有基於函數的索引。 – codeforester

1

(1)不要隱藏功能列,如果你可能需要使用一個索引:

AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day)) 

- >

AND sent_at >= CURDATE() - INTERVAL 4 day 

(2)使用的 「複合」 指數

WHERE `notification_template_id` = 175 
    AND sent_at >= ... 
    AND `user_id` IN (...) 

第一列應該是'='。目前還不清楚該怎麼把下一個,所以我建議增加這兩個指標:

INDEX(notification_template_id, user_id, sent_at) 
INDEX(notification_template_id, sent_at) 

優化器可能會在它們之間正確地挑選。

合成索引是而不是與各列上的索引相同。 (3)是的,你可能嘗試將IN列表放入tmp表中,但這樣做的成本可能會超過收益。我不認爲IN()中的1K值「太多」。

(4)My cookbook關於建築物指標。

+0

如果我需要創建composite_index INDEX(notification_template_id,user_id,sent_at),是否需要刪除單獨的索引? – Prem

+0

想想這樣......當名字列表按姓氏排序時,用_just_ firstname搜索某人是不可能的。所以,不,不要刪除任何其他索引(沒有首先發現沒有別的可能需要它們)。 –

+0

我的索引可能對_other_查詢有用或不可用。 –