出于兼容性原因,我不得不将生产数据库从 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 使用: 使用索引条件;在
,5.5 则不需要。product_index
上使用 where
我尝试通过指定 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`);
最佳答案
您的查询通过两个相等条件过滤calendar
,因此它们应该出现在彼此相同的索引中。然后,它使用 product
列访问另一个表。因此,将这三列合并为一列 compound index 。试试这个:
ALTER TABLE calendar ADD INDEX user_id_starts_at_product (user_id, starts_at, product);
您的查询对检测
执行数据范围过滤,并选择具有特定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 通常只能为每个查询(或子查询)的每个表使用一个索引。因此,添加大量单列索引通常并不是使特定查询更快的好方法。相反,添加符合查询要求的复合索引是正确的方法。对于各种数据库版本都是如此。
关于mysql - 相同的查询,MySQL 5.5 和 5.7 上的执行时间不同。 (MySQL 5.5不使用索引),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53261465/