[英]Why does a MySQL query take anywhere from 1 millisecond to 7 seconds?
我有一個SQL查詢(請參閱下文),該查詢完全返回我需要的內容,但通過phpMyAdmin運行時,則需要花費0.0009秒至0.1149秒,偶爾甚至達到7.4983秒。
查詢:
SELECT
e.id,
e.title,
e.special_flag,
CASE WHEN a.date >= '2013-03-29' THEN a.date ELSE '9999-99-99' END as date
CASE WHEN a.date >= '2013-03-29' THEN a.time ELSE '99-99-99' END as time,
cat.lastname,
FROM e_table as e
LEFT JOIN a_table as a ON (a.e_id=e.id)
LEFT JOIN c_table as c ON (e.c_id=c.id)
LEFT JOIN cat_table as cat ON (cat.id=e.cat_id)
LEFT JOIN m_table as m ON (cat.name=m.name AND cat.lastname=m.lastname)
JOIN (
SELECT DISTINCT innere.id
FROM e_table as innere
LEFT JOIN a_table as innera ON (innera.e_id=innere.id AND
innera.date >= '2013-03-29')
LEFT JOIN c_table as innerc ON (innere.c_id=innerc.id)
WHERE (
(
innera.date >= '2013-03-29' AND
innera.flag_two=1
) OR
innere.special_flag=1
) AND
innere.flag_three=1 AND
innere.flag_four=1
ORDER BY COALESCE(innera.date, '9999-99-99') ASC,
innera.time ASC,
innere.id DESC LIMIT 0, 10
) AS elist ON (e.id=elist.id)
WHERE (a.flag_two=1 OR e.special_flag) AND e.flag_three=1 AND e.flag_four=1
ORDER BY a.date ASC, a.time ASC, e.id DESC
說明計划:
問題是:此查詢的哪一部分可能導致性能差異很大?
要專門回答您的問題:不是查詢的特定部分會導致廣泛的性能。 這就是MySQL在做應做的事情-作為關系數據庫管理系統(RDBMS),而不僅僅是圍繞逗號分隔文件的愚蠢的SQL包裝器。
執行查詢時,會發生以下情況:
您所看到的是,當查詢花費0.0009秒時,緩存足夠新鮮,可以一起提供所有數據,當它在7.5秒達到峰值時,查詢表中的內容已更改,或者其他查詢“推送”了內存中緩存數據,或者DBMS有其他原因懷疑它需要重新編譯查詢或再次獲取所有數據。 可能其他一些變化與使用過的索引是否仍足夠新鮮地緩存在內存中有關。
總結一下,查詢速度非常慢,您有時會很幸運,因為緩存使查詢速度很快。
為了解決這個問題,我建議您研究以下兩點:
CASE
和COALESCE
類的編程元素通常總是有用的,但是它們確實迫使數據庫在運行時評估更多的東西,而不僅僅是獲取原始表數據。 嘗試消除此類語句,或將它們移至業務邏輯,以對檢索到的數據進行后處理。 最后,永遠不要忘記MySQL實際上是一個相當愚蠢的DBMS。 它針對大多數網站要求的簡單數據獲取查詢的性能進行了優化。 因此,對於大多數一般性問題,它比SQL Server和Oracle快得多。 一旦您開始使用功能,案例,龐大的聯接或匹配條件等使事情復雜化,競爭對手通常會得到更好的優化,並且其查詢編譯器也具有更好的優化。 因此,當MySQL在特定查詢中開始變慢時,請考慮將其拆分為2個或更多個較小的查詢,以免混淆,並使用PHP或您使用的任何語言進行一些后處理。 我已經看到很多情況下通過不混淆MySQL而提高了性能,尤其是在涉及子查詢的情況下(如您的情況)。 尤其是您的子查詢是一個派生表,而不僅僅是一個子查詢,這一事實眾所周知,它使MySQL的處理變得更加復雜。
首先,您的外部查詢和內部查詢都在使用“ e”表,且最低要求為flag_three = 1和flag_four = 1(無論內部查詢的((x和y)或z)條件如何。 WHERE子句具有對a.Flag_two的明確引用,但沒有NULL會迫使您的LEFT JOIN實際變為(INNER)JOIN。此外,當您查找“ cat”時,似乎每個“ e”記錄都必須具有一個類別。 “ lastname”,如果沒有找到,則沒有coalesce()。這似乎是對“ lookup”表的引用。對於“ m_table”和“ c_table”,您沒有使用它或對其進行任何操作,因此它們可以完全刪除。
以下查詢會為您帶來相同的結果嗎?
select
e1.id,
e1.Title,
e1.Special_Flag,
e1.cat_id,
coalesce( a1.date, '9999-99-99' ) ADate,
coalesce( a1.time, '99-99-99' ) ATime
cat.LastName
from
e_table e1
LEFT JOIN a_table as a1
ON e1.id = a1.e_id
AND a1.flag_two = 1
AND a1.date >= '2013-03-29'
JOIN cat_table as cat
ON e1.cat_id = cat.id
where
e1.flag_three = 1
and e1.flag_four = 1
and ( e1.special_flag = 1
OR a1.id IS NOT NULL )
order by
IF( a1.id is null, 2, 1 ),
ADate,
ATime,
e1.ID Desc
limit
0, 10
Main WHERE子句僅適用於那些將“三個和四個”標志設置為1 PLUS的條件(存在特殊標志或在給定日期或之后存在有效的“ a”記錄)。
由此可見,簡單的訂購受到了限制。
至於獲取日期和時間,您似乎只希望包括該日期/之后的記錄,否則將其忽略(例如,它們較舊且不適用,您不想看到它們)。
按照此順序,我正在測試FIRST以獲取“ a” ID的NULL值。 如果是這樣,我們知道他們都將被迫輸入為“ 9999-99-99”的日期和“ 99-99-99”的時間,並希望它們被推到最底端(因此2),否則,將出現“ a ”記錄下來,然后您要優先選擇這些(因此1)。 然后,分別按日期/時間排序,如果ID在同一日期/時間內有許多降序,則ID降序。
最后,為了幫助索引,我將確保您的“ e”表在以下位置具有索引
( id, flag_three, flag_four, special_flag ).
對於“ a”表,在
(e_id, flag_two, date)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.