2013-01-01 44 views
3

我的查詢目前大約需要3秒,我敢肯定可以優化。我無法弄清楚如何優化它。需要幫助優化MySQL查詢與「不在」加入

我的應用程序有一個合理的大products表(大約500,000條記錄)。每個產品可以列在50個域中的一個上(列在domains表中)。產品和域之間的鏈接存儲在domains_products表(其中約有1,400,000條記錄)中。緩慢的查詢是在我的應用程序的管理部分,我需要能夠看到沒有在任何域上列出的產品。

脫光了裸骨與所有不相關的聯接刪除,查詢有問題是:

SELECT `products`.* 
FROM  `products` 
LEFT JOIN `domains_products` 
ON  `domains_products`.`product_id` = `products`.`id` 
WHERE  `products`.`deleted` = 'N' 
AND  `domains_products`.`domain_id` IS NULL 
ORDER BY `products`.`id` ASC 

在這種形式下,查詢時間超過3秒,超過3000種產品將返回一點(這是正確的)。如果我刪除WHERE子句,查詢需要0.12秒(但顯然不會返回正確的結果)。

這兩個表都使用InnoDB引擎。 products表在id列上有一個主鍵,在deleted列上有一個索引。 domains_products表只有product_iddomain_id列,主鍵在這兩列上,並且它們都有自己的索引。所有相關的列都是NOT NULL列。

EXPLAIN給了我這樣的:

id select_type table   type possible_keys key  key_len ref   rows Extra 
1 SIMPLE  products   ref deleted  deleted 1  const  188616 Using where 
1 SIMPLE  domains_products ref product_id product_id 4  products.id 1  Using where; Using index; Not exists 

注意,雖然MySQL已經發現了正確的鍵,它實際上並不似乎可以用他們。

探查這樣說:

Status    Time 
Starting    62 µs 
Checking Permissions 7 µs 
Checking Permissions 5 µs 
Opening Tables  38 µs 
System Lock   13 µs 
Init     37 µs 
Optimizing   17 µs 
Statistics   1,3 ms 
Preparing   25 µs 
Executing   5 µs 
Sorting Result  5 µs 
Sending Data   3,3 s 
End     28 µs 
Query End   8 µs 
Closing Tables  25 µs 
Freeing Items  297 µs 
Logging Slow Query 4 µs 
Cleaning Up   5 µs 

注意,它似乎是掛在Sending Data。我嘗試用NOT IN替換連接:

SELECT `products`.* 
FROM `products` 
WHERE `products`.`deleted` = 'N' 
AND `product`.`id` NOT IN (
    SELECT `product_id` 
    FROM `domains_products` 
) 
ORDER BY `products`.`id` ASC 

此查詢給出完全相同的結果,但需要3.8秒。

任何人都可以指向正確的方向來優化此查詢嗎?

+2

Re:「請注意,雖然MySQL已經發現了正確的密鑰,但實際上並沒有使用它們」:你說的是什麼? – ruakh

+0

您是否刪除了大部分表格或進行了其他大型編輯?也許一個['OPTIMIZE TABLE'](http://dev.mysql.com/doc/refman/5.1/en/optimize-table.html)可以解決這個問題。 –

+0

@ruakh我不是閱讀EXPLAIN語法的專家,但是因爲它在Extra-column中說「使用where」,所以我假設MySQL沒有使用索引。如我錯了請糾正我。 – rickdenhaan

回答

1

看來問題在於「已刪除」列。我猜測幾乎產品表中的所有項目都標有「N」,這使得「已刪除」列的索引在這種情況下毫無用處。

你可以做的一件事是創建另一個表,比如說將存儲product_id(以及如果你想要的domain_id)的deleted_domains_products。然後,您創建一個觸發器,以便每次從domains_products中刪除條目時,都會向該表中插入條目。然後你會有一個較小的集合來查詢。當你完成後,你可以下次截斷該表,所以它應該總是很快。

+0

目前有15991個產品其中'deleted'爲「Y」,487601個產品其中'deleted'爲「N」。我會嘗試一下你的想法,讓你知道會發生什麼。 – rickdenhaan

+0

謝謝,這(主要)解決了這個問題。我添加了一個只包含已刪除產品ID的新表,並在我的「產品」表上添加了一個觸發器,以便在產品設置爲刪除時自動添加ID(反之亦然)。這已經將查詢降低到1.5秒的可管理平均值,我認爲這是可以接受的。 – rickdenhaan

0

嘗試創建以下索引,然後重新運行查詢:

  1. domains_products(產品,域ID)
  2. 產品(ID,刪除)

告訴我們如何去這個

+0

domains_products的主鍵在這兩列上。我在產品表上添加了索引,查詢花費了三次,分別是2.9,3.5和3.4秒。 – rickdenhaan

+0

恰到好處,索引是用複合字段創建的嗎? – ronpy

+0

是的,這些都是複合字段索引。 – rickdenhaan

0

試試這個,讓我知道它所花費的時間。

SELECT `products`.* 
FROM `products` 
WHERE `products`.`deleted` = 'N' 
AND NOT EXISTS (SELECT 1 
       FROM `domains_products` 
       WHERE `domains_products`.`product_id` = `products`.`id` 
      ); 
ORDER BY `products`.`id` ASC 
+0

該查詢需要0.0015秒,但返回不正確的結果。如果我翻轉: SELECT'products' * FROM'products' WHERE'products'.'deleted' = 'N' AND NOT EXISTS(SELECT 1 FROM'domains_products' WHERE'domains_products'.'。產品''''' ) ORDER BY' – rickdenhaan