出于兼容性原因,我不得不将生产数据库从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`);
答案 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`;
这确实在运行时锁定了表,因此请记住生产数据库上的表。
最后,spot
。product
是calendar
。product
的外键吗?反之亦然?它们是完全相同的数据类型吗?