相同的查询,在MySQL 5.5和5.7上执行时间不同。 (MySQL 5.5未使用索引)

时间:2018-11-12 11:44:35

标签: mysql sql query-optimization query-performance

出于兼容性原因,我不得不将生产数据库从MySQL 5.7降级到MySQL 5.5。

移至5.5后,我注意到此查询的执行速度大大降低,从大约200毫秒变为大约20秒的执行时间。

以下是查询:

SELECT
  COUNT(*)
FROM
  `calendar`
INNER JOIN
  `spot` ON `spot`.`product` = `calendar`.`product`
        AND `spot`.`company_id` = `calendar`.`company_id`
INNER JOIN
  `detection` ON `detection`.`spot_id` = `spot`.`id`
WHERE `calendar`.`starts_at` = '2017-11-17'
  AND `calendar`.`user_id` = 73
  AND `detection`.`date` >= '2017-11-17'
  AND `detection`.`date` <= '2017-11-23'

这是MySQL 5.5的EXPLAIN输出:

1 SIMPLE | calendar | ref starts_at_ends_at_index starts_at_ends_at_index 3 const 1204 | Using where
1 SIMPLE | spot ref PRIMARY,company_id_index,product_index | product_index | 302 calendar.product | 13 | Using where
1 SIMPLE | detection | ref spot_id_index,date_index | spot_id_index 48 | spot.Id | 80 | Using where

这是MySQL 5.7的EXPLAIN输出:

1 SIMPLE | calendar | ref starts_at_ends_at_index starts_at_ends_at_index 3 const 1204 | Using where
1 SIMPLE | spot ref PRIMARY,company_id_index,product_index | product_index | 302 calendar.product | 13 | Using index condition; Using where
1 SIMPLE | detection | ref spot_id_index,date_index | spot_id_index 48 | spot.Id | 80 | Using where

我唯一看到的区别是MySQL 5.7使用了Using index condition; Using where上的product_index,而不是5.5。

我尝试通过指定USE INDEX(product_index)来强制使用索引,但是没有任何改变

有什么建议吗?

编辑:

当前有用的索引:

ALTER TABLE `calendar` ADD INDEX `starts_at_ends_at_index` (`starts_at`, `ends_at`);

ALTER TABLE `spot` ADD INDEX `company_id_index` (`company_id`);

ALTER TABLE `spot` ADD INDEX `product_index` (`product`);

ALTER TABLE `detection` ADD INDEX `spot_id_index` (`spot_id`);

ALTER TABLE `detection` ADD INDEX `date_index` (`date`);

2 个答案:

答案 0 :(得分:1)

您的查询通过两个相等条件过滤calendar,因此它们应出现在相同的索引中。然后,它使用product列访问另一个表。因此,将这三列放在一个compound index中。试试这个:

 ALTER TABLE calendar ADD INDEX user_id_starts_at_product (user_id, starts_at, product);

您的查询对detection进行数据范围过滤,还选择具有特定值spot_id的行。因此,请尝试使用此复合索引。

 ALTER TABLE detection ADD INDEX spot_id_date (spot_id, date);

还尝试以相反的顺序使用列的复合索引,并保留可为您提供更好性能的索引。

 ALTER TABLE detection ADD INDEX date_spot_id (date, spot_id);

尝试在spot上使用复合索引以覆盖这两个过滤条件(出现在ON子句中)。

  ALTER TABLE spot ADD INDEX company_id_product (company_id, product);

专业提示:MySQL通常每个查询(或子查询)的每个表只能使用一个索引。因此,添加大量单列索引通常不是使特定查询更快的好方法。相反,添加符合查询要求的复合索引才是正确的方法。各种数据库版本都是如此。

答案 1 :(得分:0)

我会尝试将不将日历表过滤到联接谓词中的where子句谓词移至其他位置,如果它没有其他帮助提高可读性,但也可以帮助引擎编译更优化的计划。

SELECT 
    COUNT(*)
FROM
    `calendar`
INNER JOIN `spot` 
    ON `spot`.`product` = `calendar`.`product` 
    AND `spot`.`company_id` = `calendar`.`company_id`
INNER JOIN `detection` 
    ON `detection`.`spot_id` = `spot`.`id`
    AND `detection`.`date` BETWEEN '2017-11-17' AND '2017-11-23'  
WHERE
    `calendar`.`starts_at` = '2017-11-17' 
    AND `calendar`.`user_id` = 73

降级之后索引也可能需要重建,您可以使用下面的表格对每个表执行此操作。

OPTIMIZE TABLE `calendar`;
OPTIMIZE TABLE `spot`;
OPTIMIZE TABLE `detection`;

这确实在运行时锁定了表,因此请记住生产数据库上的表。

最后,spotproductcalendarproduct的外键吗?反之亦然?它们是完全相同的数据类型吗?