sql—相同的查询,在MySQL5.5和5.7上的执行时间不同(MySQL5.5(不使用索引)

lokaqttq  于 2021-06-18  发布在  Mysql
关注(0)|答案(2)|浏览(465)

出于兼容性原因,我不得不将一个生产数据库从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'

以下是MySQL5.5的解释输出:

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

以下是MySQL5.7的解释输出:

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

我能看到的唯一区别是MySQL5.7使用了: Using index condition; Using whereproduct_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`);
cig3rfwq

cig3rfwq1#

我会尝试将不过滤日历表的where子句 predicate 移动到join predicate 中,如果没有其他帮助的话,它可以帮助可读性,但也可以帮助引擎编译一个更优化的计划。

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`;

不过,在运行时它确实会锁定表,所以在生产数据库中要记住这一点。
最后,是 spot . product 的外键 calendar . product 反之亦然?它们是完全相同的数据类型吗?

jutyujz0

jutyujz02#

您的查询筛选器 calendar 两个相等的标准,所以它们应该出现在同一个索引中。然后使用 product 列来访问另一个表。所以,把这三列放在一个复合索引中。试试这个:

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通常只能为每个查询(或子查询)的每个表使用一个索引。因此,添加大量的单列索引通常不是加快特定查询速度的好方法。相反,添加符合查询要求的复合索引是一种可行的方法。对于各种数据库版本都是如此。

相关问题