2016-11-29 90 views
0

我在Debian 8機器上使用mysql 5.5.52,有時候我們有一個慢速查詢(> 3s),通常花費0.1s。我已經開始使用explain命令來查找正在發生的事情。mysql優化器中的奇怪查詢

這是查詢和解釋信息

explain 
SELECT 
    `box`.`message_id` ID 
, `messages`.`tipo` 
, `messages`.`text` 
, TIME_TO_SEC(TIMEDIFF(NOW(), `messages`.`date`)) `date` 
FROM (`box`) 
INNER JOIN `messages` ON `messages`.`id` = `box`.`message_id` 
WHERE `box`.`user_id` = '1010231' AND `box`.`deleted` = 0 
    AND `messages`.`deleted` = 0 
    AND `messages`.`date` + INTERVAL 10 MINUTE > NOW() 
ORDER BY `messages`.`id` ASC LIMIT 100; 


id| select_type| table | type | possible_keys | key | key_len| ref   | rows | Extra  
1|SIMPLE  |box  |ref |user_id,message_id|user_id|  4|const   | 2200 |Using where; Using temporary; Using filesort 
1|SIMPLE  |messages|eq_ref|PRIMARY   |PRIMARY|  4|box.message_id| 1 |Using where 

我知道,臨時表和文件排序是一件壞事,我想這個問題是爲了關鍵不屬於第一個表查詢(盒),並改變它box.message_id,該解釋信息是

ID,SELECT_TYPE,表,類型,possible_keys,鑰匙,key_len,裁判,行,額外

1簡單的方塊指數用戶名, MESSAGE_ID MESSAGE_ID 4 443使用其中

1點,簡單的信息eq_ref PRIMARY PRIMARY 4 box.message_id 1使用其中

它看起來更好,但我不明白爲什麼它使用MESSAGE_ID指標,最壞的情況,現在查詢需要1.5秒,而不是最初的0.1秒

編輯:

強制查詢中使用USER_ID指數,我得到了相同的結果(0.1秒)作爲初始查詢,但沒有臨時

explain 
SELECT 
    `box`.`message_id` ID 
, `messages`.`tipo` 
, `messages`.`text` 
, TIME_TO_SEC(TIMEDIFF(NOW(), `messages`.`date`)) `date` 
FROM (`box` use index(user_id)) 
INNER JOIN `messages` ON `messages`.`id` = `box`.`message_id` 
WHERE `box`.`user_id` = '1010231' AND `box`.`deleted` = 0 
    AND `messages`.`deleted` = 0 
    AND `messages`.`date` + INTERVAL 10 MINUTE > NOW() 
ORDER BY `box`.`message_id` ASC LIMIT 100; 

id| select_type| table | type | possible_keys | key | key_len| ref   | rows | Extra  
1|SIMPLE  |box  |ref |user_id,message_id|user_id|  4|const   | 2200 |Using where; Using filesort 
1|SIMPLE  |messages|eq_ref|PRIMARY   |PRIMARY|  4|box.message_id| 1 |Using where 

我認爲跳過臨時比初始查詢更好的解決方案,下一步是檢查組合索引,因爲ysth建議。

+0

你能顯示產生第二個解釋的查詢嗎? – ysth

+0

啊,你的意思是你改變了從message.id到box.message_id的順序? – ysth

+0

'AND \'messages \'。() - INTERVAL 10分鐘> NOW()' - >'AND \'messages \'。\'date \'> NOW() - INTERVAL 10 MINUTE'。當不需要時不要對列進行任何轉換。 (在這種情況下,這可能不是問題,但只是一個參考)。 – Pred

回答

0

這是不是一個好主意來計算fieldvalues比較。那麼你會得到一個完整的表掃描。在檢查條件之前,MySQL必須爲每個ROW執行此操作。在不變的條件下做它更好。那麼MySQL可以使用一個索引(如果有一個在此領域)

變化從

AND messages.date + INTERVAL 10 MINUTE > NOW() 

AND messages.date > NOW() - INTERVAL 10 MINUTE 
+0

但他沒有得到全表掃描;日期不是這裏的索引。 – ysth

+0

我知道計算不適用於索引,但該字段(日期)沒有索引,並且在修改後的查詢中,優化器正在使用索引,但對於我來說是錯誤的(message_id) – OscarGz

+0

好吧,將'INDEX(user_id,刪除,日期)' - 然後tmp + filesort +表掃描將消失。 –

0

臨時和文件的排序是壞在這裏;他們是需要的,因爲使用最好的索引(user_id)不會自然地產生按您要求的順序排序的記錄。

有可能你可以做更好的結合user_id,message_id索引,但這可能也會變得更糟。取決於您的確切數據。

對於某些用戶標識或相同的用戶標識,如果您看到更長的查詢有時花費更長時間,我不清楚。

更新:通過box.user_id,box.message_id組合索引和更改順序似乎可能會解決您的問題,至少對於沒有大量已刪除郵件的用戶而言。

+0

已更新的答案。 – ysth

+0

緩慢的查詢是「隨機的」,如果我多次執行相同的查詢,通常工作正常,但有時我有一個緩慢的查詢。查詢不是問題,但我已經開始嘗試優化它。 – OscarGz

+0

我不明白爲什麼它使用message_id作爲索引只有當我使用它來排序時,message.id優化器使用user_id的順序,它就像按索引排序優先於where索引。 – OscarGz