需要帮助优化MySQL查询与“不在”加入

时间:2013-01-01 17:33:13

标签: mysql optimization join notin

我的查询目前大约需要3秒钟,我相信我可以对其进行优化。我只是想不出如何优化它。

我的应用程序有一个相当大的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秒以上的时间并返回3,000多个产品(这是正确的)。如果我删除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秒。

有人能指出我正确的方向来优化此查询吗?

3 个答案:

答案 0 :(得分:1)

似乎问题出在“已删除”列中。我猜测产品表中的几乎所有项目都标有“N”,这使得“已删除”列中的索引在这种情况下毫无用处。

你可以做的一件事就是创建另一个表,比如说存储了product_id的deleted_domains_products(以及你想要的domain_id)。然后创建一个触发器,这样每次从domains_products中删除一个条目时,它都会在该表中插入一个条目。那么你将有一个较小的集合来查询。当你完成后,你可以在下次截断该表,所以它总是很快。

答案 1 :(得分:0)

尝试创建以下索引,然后重新运行查询:

  1. domains_products(product_id,domain_id)
  2. 产品(id,已删除)
  3. 告诉我们这是怎么回事

答案 2 :(得分: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