Тот же запрос, разное время выполнения на MySQL 5.5 и 5.7. (MySQL 5.5 не использует индекс) - PullRequest
0 голосов
/ 12 ноября 2018

По соображениям совместимости мне пришлось понизить производственную базу данных с 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'

Вот вывод EXPLAIN для MySQL 5.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

Вот вывод EXPLAIN для MySQL 5.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

Единственное отличие, которое я вижу, в том, что MySQL 5.7 использует: Using index condition; Using where на product_index, 5.5 - нет.

Я пытался принудительно использовать индекс, указав USE INDEX(product_index), но ничего не изменилось

Есть предложения?

EDIT:

Текущие полезные индексы:

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 голосов
/ 12 ноября 2018

Ваш запрос фильтрует 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);

Pro tip : MySQL обычно может использовать только один индекс для каждой таблицы на запрос (или подзапрос). Поэтому добавление большого количества одноколоночных индексов, как правило, не является хорошим способом ускорения выполнения определенных запросов. Вместо этого добавьте составные индексы, соответствующие требованиям вашего запроса. Это верно для различных версий баз данных.

0 голосов
/ 12 ноября 2018

Я бы попытался переместить предикаты предложения 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 или наоборот?Это точно такой же тип данных?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...