簡體   English   中英

MySQL:長時間運行的 LEFT JOIN 查詢性能

[英]MySQL: long running LEFT JOIN query performance

一個 MySQL 數據庫包含兩個表: customercustmomer_orders

客戶表包含 8000 萬個條目和 80 個字段。 其中一些我感興趣:

  1. Id (PK, int(10))
  2. 位置(varchar 255,可為空)。
  3. Registration_Date(日期時間,可為空)。 索引。

customer_orders表包含 4000 萬個條目,並且僅包含 3 個字段:

  1. Id (PK, int(10))
  2. Customer_Id (int(10), FK to customer table)
  3. Order_Date(日期時間,可為空)

當我運行這樣的查詢時,執行大約需要800秒並返回 4000 萬個條目:

SELECT o.* 
FROM customer_orders o
LEFT JOIN customer c ON (c.Id = o.Customer_Id) 
WHERE NOT (ISNULL(c.Location)) AND c.Registration_Date < '2018-01-01 00:00:00';

帶有 MySQL 服務器的機器有 32GB 的 RAM,28GB 分配給 MySQL。 MySQL 版本:5.6.39。

MySQL 在具有如此多記錄的表上執行如此長時間的此類查詢是否正常? 如何提高性能?

更新:

customer_orders 表不包含我們想要存儲的任何重要數據。 這是某種復制表,其中包含過去 10 天內下的訂單。 我們每天都運行一個存儲過程,它會刪除事務范圍內超過 10 天的訂單。

有一段時間,這個存儲過程因為沒有優化查詢而超時,訂單數量每天都在增長。 上一個查詢還包含 COUNT 方法,我想這超出了超時時間。

然而,令我驚訝的是,MySQL 可能需要長達 15 分鍾才能在附加條件下獲取 40m 的記錄。

我覺得很正常。 如果您分享該查詢的explain返回內容,將會很有幫助。

為了優化查詢,從 customer_orders 開始可能不是一個好主意,因為無論如何您都沒有對其進行過濾(因此它正在執行超過 40M 記錄的全表掃描)。 此外,正如評論中所指出的,這里不需要LEFT JOIN 我會這樣寫你的查詢:

SELECT o.*
FROM customers c, customer_orders o
WHERE c.id = o.Customer_Id
AND   c.Location IS NOT NULL
AND   c.Registration_Date < '2018-01-01'

這將(取決於多條記錄如何滿足條款Registration_Date < '2018-01-01' )篩選customers第一個表,然后用加入customer_orders其中有表和索引的customer_id

另外,可能不相關,但是查詢返回 40M 記錄對您來說是否正常? 我的意思是,這就像整個customer_orders表。 如果我是對的,這意味着所有訂單都來自“2018-01-01”之前注冊的客戶

這是太渴望評論了...

關於您的查詢,首先要注意的是它實際上並未執行LEFT JOIN ,因為它在WHERE子句中具有引用LEFT JOIN ed 表的條件。

可以改寫為:

SELECT o.* 
FROM customer_orders o
INNER JOIN customer c 
    ON c.Id = o.Customer_Id
    AND c.Location is NOT NULL
    AND c.Registration_Date < '2018-01-01 00:00:00';

明確連接類型更有利於可讀性,並且可能有助於 MySQL 為查詢找到更好的執行路徑。

在性能方面,基本建議是,對於此查詢,您需要在所有被搜索的三列上建立一個復合索引,其順序與查詢中使用的列相同(通常,您想放置更多開始時的限制條件,因此您可能需要對此進行調整):

ALTER TABLE mytable ADD INDEX (Id, Location, Registration_Date );

有關性能的更多建議,您可能希望使用CREATE TABLECREATE TABLE語句和查詢的執行計划來更新您的問題。

如果我的評論和 GMB 的回答最終對性能沒有太大幫助; 您可以隨時嘗試使用不同的方法編寫查詢。 我通常更喜歡連接到子查詢,但有時它們會成為處理數據的最佳選擇。

由於您已經說過與訂單表相比,客戶表相對較大,這可能是其中一種情況。

SELECT o.* 
FROM customer_orders AS o
WHERE o.Customer_Id IN (
     SELECT Id 
     FROM customer 
     WHERE Location IS NOT NULL 
        AND Registration_Date < '2018-01-01 00:00:00'
);

我想發表評論,但改變主意要回答。

因為主要問題是您的問題本身。

我不知道你的customer_orders有多少列,但如果你得到

4000 萬個條目

背部。 我會說你做錯了什么。 可能這不是查詢本身很慢,而是數據獲取。

為了證明嘗試對您的查詢執行EXPLAIN

EXPLAIN SELECT ...your query here... ;

然后執行

EXPLAIN SELECT ...your query here... LIMIT 1;

嘗試將結果LIMIT為 1000,例如:

SELECT ...your query here... LIMIT 1000;

當您有這些查詢的答案、輸出和統計數據時,我們可以討論您的以下步驟。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM