【问题标题】:Why is this MySQL query incredibly slow and not using the right index?为什么这个 MySQL 查询非常慢并且没有使用正确的索引?
【发布时间】:2018-02-02 13:27:16
【问题描述】:

这需要大约 100 秒来执行:

SELECT part.*
FROM   (((((((`part` 
              LEFT JOIN `engine_x_category_1_x_category_2_x_part` 
                     ON `engine_x_category_1_x_category_2_x_part`.`part_id` = 
                      `part`.`id`) 
             LEFT JOIN `engine_x_category_1_x_category_2` 
                    ON 
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id` 
= `engine_x_category_1_x_category_2`.`id`) 
LEFT JOIN `engine_x_category_1` 
       ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` = 
          `engine_x_category_1`.`id`) 
 LEFT JOIN `engine` 
        ON `engine_x_category_1`.`engine_id` = `engine`.`id`) 
LEFT JOIN `model` 
       ON `engine`.`model_id` = `model`.`id`) 
 LEFT JOIN `year` 
        ON `model`.`year_id` = `year`.`id`) 
LEFT JOIN `make` 
       ON `year`.`make_id` = `make`.`id`) 
LEFT JOIN `category_1` 
      ON `engine_x_category_1`.`category_1_id` = `category_1`.`id` 
WHERE  1 > 0 
       AND `category_1`.`id` > 0 
GROUP  BY `part`.`id` 
ORDER  BY `part`.`id` ASC 
LIMIT  0, 20 

然而,一旦我们摆脱了AND category_1.id > 0,它只需要 0.003 秒。

这真的很奇怪,因为 category_1.id 是 PRIMARY KEY 列,而表 category_1 只有 26 行。

category_1的结构和键

查询解释:

这里可能有什么问题?为什么 MySQL 不对category_1.id 使用 PRIMARY 索引,而是使用用于时间戳列的索引 ts

附: MySQL 版本:5.6.33

更新 1

正如 O. Jones 指出的那样,我尝试使用表 category_1 使用 FORCE INDEX (PRIMARY),并且 EXPLAIN 显示查询正确地使用 PRIMARY 而不是 ts 作为 category_1.id 的索引,但查询仍然需要将近 100 秒才能执行。

似乎category_1.id 不是这里的罪魁祸首。

更新 2

在@O.Jones 的建议下,我去掉了makeyearmodelengine 上不相关的JOIN:

SELECT part.*
FROM   (((`part` 
              LEFT JOIN `engine_x_category_1_x_category_2_x_part` 
                     ON `engine_x_category_1_x_category_2_x_part`.`part_id` = 
                      `part`.`id`) 
             LEFT JOIN `engine_x_category_1_x_category_2` 
                    ON 
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id` 
= `engine_x_category_1_x_category_2`.`id`) 
LEFT JOIN `engine_x_category_1` 
       ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` = 
          `engine_x_category_1`.`id`) 
LEFT JOIN `category_1` 
      ON `engine_x_category_1`.`category_1_id` = `category_1`.`id` 
WHERE  1 > 0 
       AND `category_1`.`id` > 0 
GROUP  BY `part`.`id` 
ORDER  BY `part`.`id` ASC 
LIMIT  0, 20

现在查询只需要大约 0.003 秒。

虽然这回答了这个特定查询的问题,但它显然 不是 最终解决方案,因为人们可能还需要通过 both @ 987654348@ 和 make,因此无法删除查询中的任何 JOIN:

SELECT part.*
FROM   (((((((`part` 
              LEFT JOIN `engine_x_category_1_x_category_2_x_part` 
                     ON `engine_x_category_1_x_category_2_x_part`.`part_id` = 
                      `part`.`id`) 
             LEFT JOIN `engine_x_category_1_x_category_2` 
                    ON 
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id` 
= `engine_x_category_1_x_category_2`.`id`) 
LEFT JOIN `engine_x_category_1` 
       ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` = 
          `engine_x_category_1`.`id`) 
 LEFT JOIN `engine` 
        ON `engine_x_category_1`.`engine_id` = `engine`.`id`) 
LEFT JOIN `model` 
       ON `engine`.`model_id` = `model`.`id`) 
 LEFT JOIN `year` 
        ON `model`.`year_id` = `year`.`id`) 
LEFT JOIN `make` 
       ON `year`.`make_id` = `make`.`id`) 
LEFT JOIN `category_1` 
      ON `engine_x_category_1`.`category_1_id` = `category_1`.`id` 
WHERE  1 > 0 
       AND `category_1`.`id` > 0
       AND `make`.`id` > 0
GROUP  BY `part`.`id` 
ORDER  BY `part`.`id` ASC 
LIMIT  0, 20

经过多次测试,仍然可以运行大约 80 - 100 秒。

我们现在应该做什么?

更新 3

按照@LuisMuñoz 的建议,我将条件从 WHERE 移至相应的 JOIN ON 子句:

SELECT part.*
FROM   (((((((`part` 
              LEFT JOIN `engine_x_category_1_x_category_2_x_part` 
                     ON `engine_x_category_1_x_category_2_x_part`.`part_id` = 
                      `part`.`id`) 
             LEFT JOIN `engine_x_category_1_x_category_2` 
                    ON 
`engine_x_category_1_x_category_2_x_part`.`engine_x_category_1_x_category_2_id` 
= `engine_x_category_1_x_category_2`.`id`) 
LEFT JOIN `engine_x_category_1` 
       ON `engine_x_category_1_x_category_2`.`engine_x_category_1_id` = 
          `engine_x_category_1`.`id`) 
 LEFT JOIN `engine` 
        ON `engine_x_category_1`.`engine_id` = `engine`.`id`) 
LEFT JOIN `model` 
       ON `engine`.`model_id` = `model`.`id`) 
 LEFT JOIN `year` 
        ON `model`.`year_id` = `year`.`id`) 
JOIN `make` 
  ON `year`.`make_id` = `make`.`id` 
     AND `make`.`id` > 0) 
JOIN `category_1` 
 ON `engine_x_category_1`.`category_1_id` = `category_1`.`id` 
    AND `category_1`.`id` > 0 
WHERE  1 > 0 
GROUP  BY `part`.`id` 
LIMIT  0, 10 

必须使用 JOIN 而不是 LEFT JOIN 否则条件不会生效。

但是它仍然和 WHERE 子句中的条件一样慢。

【问题讨论】:

  • 请格式化查询。不可读。
  • engine_x_category_1.category_1_id 上是否有索引,查询正在扫描整个 category_1 表 4319/26 次。
  • @LuisMuñoz 是的,engine_x_category_1.category_1_id 被索引为非唯一索引。
  • SELECT part.id AS part.id, .... part.category_2 AS part.category_2 .... GROUP BY part.id 是无效的 SQL 查询并导致无效的结果。阅读(psce.com/en/blog/2012/05/15/…
  • category_1 表上的索引无关紧要。它的行数太少,以至于索引访问无关紧要。您的 parts 表上的索引可能会有所不同,也许在那些具有长名称的 _category_ 表上。请注意,您的 LEFT JOINcategory_1 通过引用 WHERE 子句中的列转换为普通的内部 JOIN。您的查询 SELECTs 在您的 LEFT JOIN 操作中提到的某些表中没有列,因此您至少可以简化查询以进行性能故障排除。

标签: mysql database performance query-performance


【解决方案1】:

尝试加快查询速度的建议很少(我查看了上次更新,#3):

  • 确保查询不会返回太多行(当 X 是您定义的合理数字时,用户不需要查看超过 X 行)。最好通过添加过滤条件(已编入索引)来过滤行,但您也可以添加 LIMIT 子句。
  • 添加索引以加快搜索速度(此查询的推荐索引如下)。
  • 您正在使用 LEFT 加入 category_1,但您正在过滤该表的 id (category_1.id > 0)。因此,您可以将单词 LEFT to INNER JOIN 删除到该表中,这可能会加快查询速度。

添加以下索引:

ALTER TABLE `category_1` ADD INDEX `category_1_idx_id` (`id`);
ALTER TABLE `engine_x_category_1` ADD INDEX `engine_x_category_1_idx_id` (`category_1_id`);
ALTER TABLE `make` ADD INDEX `make_idx_id` (`id`);
ALTER TABLE `part` ADD INDEX `part_idx_id` (`id`);

【讨论】:

    猜你喜欢
    • 2013-07-09
    • 1970-01-01
    • 2014-10-16
    • 2014-07-20
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多