2

我的查询目前大约需要 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 秒。

谁能指出我优化此查询的正确方向?

4

3 回答 3

1

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

您可以做的一件事是创建另一个表,例如deleted_domains_products,它将存储product_id(如果需要,还有domain_id)。然后您创建一个触发器,以便每次从 domain_products 中删除一个条目时,它都会在该表中插入一个条目。然后你将有一个较小的集合来查询。完成后,您可以在下一次截断该表,所以它应该总是很快。

于 2013-01-02T03:17:14.710 回答
0

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

  1. domain_products (product_id, domain_id)
  2. 产品(id,已删除)

告诉我们这是怎么回事

于 2013-01-01T17:44:08.243 回答
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
于 2013-01-01T18:09:14.833 回答