为什么这个MySQL查询速度极慢而且没有使用正确的索引?

时间:2018-02-02 13:27:16

标签: mysql database performance query-performance

这需要大约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

的结构和键

enter image description here

查询EXPLAIN:

MySQL EXPLAIN of this query

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

P.S。 MySQL版本:5.6.33

更新1

正如O.Jones指出的那样,我尝试FORCE INDEX (PRIMARY)使用表category_1并且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

EXPLAIN

现在查询只占用 0.003 秒。

虽然这回答了这个特定查询的问题,但它显然是 NOT 最终解决方案,因为人们可能还需要通过两者来搜索汽车零件 { {1}}和category_1,因此无法删除查询中的任何JOIN:

make

EXPLAIN

经过多次测试后,仍然会持续约 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`) 
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

EXPLAIN

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

然而,当条件在WHERE子句中时,它仍然像SLOW一样慢。

1 个答案:

答案 0 :(得分:0)

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

  • 确保查询不会返回太多行(当X是您定义的合理数字时,用户无需查看超过X行的数据)。最好通过添加过滤条件(已编制索引)来过滤行,但您也可以添加LIMIT子句。
  • 添加索引以加快搜索速度(此查询的推荐索引如下所示)。
  • 您正在使用LEFT加入category_1,但您正在过滤该表的ID(category_1.id > 0)。因此,您可以将LEFT一词移除到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`);