2012-04-26 26 views
3

在一個MySQL 5.1.34服務器,我有以下困窘的形勢:爲什麼mysql決定這個子查詢是相關的?

mysql> explain select * FROM master.ObjectValue WHERE id IN (SELECT id FROM backup.ObjectValue) AND timestamp < '2008-04-26 11:21:59'; 
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+ 
| id | select_type  | table  | type   | possible_keys            | key        | key_len | ref | rows | Extra  | 
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+ 
| 1 | PRIMARY   | ObjectValue | range   | IX_ObjectValue_Timestamp,IX_ObjectValue_Timestamp_EventName | IX_ObjectValue_Timestamp_EventName | 9  | NULL | 541944 | Using where | 
| 2 | DEPENDENT SUBQUERY | ObjectValue | unique_subquery | PRIMARY              | PRIMARY       | 4  | func |  1 | Using index | 
+----+--------------------+-------------+-----------------+-------------------------------------------------------------+------------------------------------+---------+------+--------+-------------+ 
2 rows in set (0.00 sec) 

mysql> select * FROM master.ObjectValue WHERE id IN (SELECT id FROM backup.ObjectValue) AND timestamp < '2008-04-26 11:21:59'; 
Empty set (2 min 48.79 sec) 

mysql> select count(*) FROM master.ObjectValue; 
+----------+ 
| count(*) | 
+----------+ 
| 35928440 | 
+----------+ 
1 row in set (2 min 18.96 sec) 
  • 怎麼會需要3分鐘檢查50萬個記錄時,只需要 2分鐘訪問所有記錄?
  • 如何將 上的子查詢分開獨立的數據庫進行分類?
  • 我該怎麼做才能加快 這個查詢?

UPDATE:

是花了很長一段時間的實際查詢是DELETE,但你不能做的解釋; DELETE是我爲什麼使用子選擇。我現在有閱讀文檔,發現有關語法「DELETE FROM T使用......」從改寫查詢:

DELETE FROM master.ObjectValue 
WHERE timestamp < '2008-06-26 11:21:59' 
AND id IN (SELECT id FROM backup.ObjectValue) ; 

到:

DELETE FROM m 
USING master.ObjectValue m INNER JOIN backup.ObjectValue b ON m.id = b.id 
WHERE m.timestamp < '2008-04-26 11:21:59'; 

減少時間從幾分鐘.01秒空的備份.ObjectValue。

謝謝大家的好建議。

+0

一個COUNT(*)要快很多。是master.ObjectValue表或視圖? – Johan 2012-04-26 09:54:30

回答

3

注意它怎麼說的只有1行中的子查詢?顯然有超過1排。這表示mysql一次只加載1行。 mysql可能試圖做的是「優化」子查詢,以便它僅加載子查詢中也存在於主查詢中的記錄,即相關子查詢。這是一個連接的工作方式,但是你表達查詢的方式迫使優化的連接邏輯發生逆轉。

你已經告訴mysql加載備份表(子查詢),然後將它與主表的過濾結果「timestamp <'2008-04-26 11:21:59'」相匹配。 Mysql確定加載整個備份表可能不是一個好主意。因此,mysql決定使用主服務器的過濾結果來過濾備份查詢,但主查詢在嘗試過濾子查詢時尚未完成。所以它需要檢查,因爲它從主查詢中加載每條記錄。因此你的依賴子查詢。

正如其他人所提到的,使用聯接,它以正確的方式去。加入人羣。

5

從屬子查詢減慢你的外部查詢下來爬行(我想你知道它意味着它在運行的數據集中找到的每行運行一次)。

你不需要有子查詢,而不是使用一個很有顯著加速您的查詢:

SELECT m.* 
FROM master.ObjectValue m 
JOIN backup.ObjectValue USING (id) 
WHERE m.timestamp < '2008-06-26 11:21:59' 

的MySQL經常把子查詢的依賴,即使他們都沒有。我從來沒有真正理解這個的確切原因 - 也許這只是因爲查詢優化器無法將其識別爲獨立的。我從來沒有打擾過細看,因爲在這些情況下,你幾乎總是可以將它移動到FROM條款,該條款修復了它。

例如:

DELETE FROM m WHERE m.rid IN (SELECT id FROM r WHERE r.xid = 10) 
// vs 
DELETE m FROM m WHERE m.rid IN (SELECT id FROM r WHERE r.xid = 10) 

前者會產生依賴性的子查詢可能會很慢。後者會告訴優化器隔離子查詢,從而避免表掃描並使查詢運行得更快。

+0

錯誤47914應該是在MySQL 5.6.5已經解決了這一點:http://bugs.mysql.com/bug.php?id=47914我還沒有嘗試過自己的版本,但它看起來很有希望。我已經在Oracle中使用了非依賴的子選擇了幾十年,但沒有這種性能影響(有些應用程序讓您別無選擇,只能編寫一些像這樣的查詢,而且在處理「企業中的許多小查找數據集時」 '應用程序)。 – Jason 2014-02-05 12:15:35

3

當訪問所有記錄只需2分鐘時,如何檢查500000條記錄需要3分鐘?

COUNT(*)總是在MySQL中轉換爲COUNT(1)。所以它甚至不需要輸入每條記錄,而且我可以設想它使用內存索引來加快速度。在長時間運行的查詢中,您使用範圍(<)和IN運算符,因此對於它訪問的每條記錄,必須執行額外的工作,尤其是因爲它將子查詢識別爲從屬。

如何將單獨數據庫上的子查詢分類爲依賴項?

那麼,如果它在一個單獨的數據庫中沒有關係。如果子查詢依賴於來自外部查詢的值,那麼子查詢是依賴的,在您的情況下您仍然可以執行該查詢......但是您不這樣做,所以確實很奇怪它被歸類爲從屬子查詢。也許這只是MySQL中的一個bug,這就是爲什麼它需要這麼長時間 - 它爲外部查詢選擇的每個記錄執行內部查詢。

我能做些什麼,以加快此查詢?

首先,請嘗試使用JOIN代替:

SELECT master.* 
FROM master.ObjectValue master 
JOIN backup.ObjectValue backup 
    ON master.id = backup.id 
    AND master.timestamp < '2008-04-26 11:21:59'; 
+0

您的SQL將返回所有重複的列(一次來自備份,一次來自主服務器)。 – Romain 2012-04-26 09:58:36

+0

@Romain你是對的。把它改成'master。*'。但我認爲這並不重要。 – 2012-04-26 10:11:33

+0

返回的數據越少,結果集的傳輸速度越快,消耗的內存也越少。但我同意這種影響比刪除子查詢的影響要小得多。 – Romain 2012-04-26 10:23:13

0

真正的答案是,不使用MySQL,它的優化是垃圾。切換到Postgres,從長遠來看,它將爲您節省時間。

對大家說:「使用JOIN」,這只是由MySQL的人羣中誰拒絕了10年來解決這個離譜可怕的錯誤延續廢話。