2013-01-01 43 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