MySql запрос не использует набор индексов - PullRequest
0 голосов
/ 13 мая 2018

Недавно мы переключили db с MariaDB 10.2 на Percona Server (Mysql 5.7), и у нас есть запрос, который занимает около 15 секунд (прежде чем он был около 0,5), потому что оптимизатор запросов не использует какой-либо индекс в главной таблице. Поскольку в логике приложения мы не можем изменить формат запроса, нам нужно заставить БД использовать индекс.

Структура запроса проста:

EXPLAIN SELECT `clients`.`id` AS `t0_c0`,
       `client`.`name1` AS `t0_c1`,
       `client`.`name2` AS `t0_c2`,
       `users`.`id` AS `t1_c0`,
       `users`.`suffix` AS `t1_c1`,
       `users`.`position` AS `t1_c2`,
       `users`.`first_name` AS `t1_c3`,
       `users`.`last_name` AS `t1_c4`,
       `privateData`.`id` AS `t2_c0`,
       `privateData`.`first_name` AS `t2_c1`,
       `privateData`.`last_name` AS `t2_c2`,
       `tariff`.`id` AS `t3_c0`,
       `tariff`.`provider_id` AS `t3_c1`,
       `tariff`.`tariff_type` AS `t3_c2`,
       `tariff`.`name` AS `t3_c3`,
       `providers`.`id` AS `t4_c0`,
       `providers`.`name1` AS `t4_c1`,
       `providers`.`name2` AS `t4_c2`,
       `addresses`.`id` AS `t5_c0`,
       `addresses`.`zipcode` AS `t5_c1`,
       `addresses`.`country` AS `t5_c2`,
       `addresses`.`city` AS `t5_c3`,
       `private`.`id` AS `t6_c0`,
       `private`.`first_name` AS `t6_c1`,
       `private`.`last_name` AS `t6_c2`,
       `commercial`.`id` AS `t7_c0`,
       `commercial`.`name1` AS `t7_c1`,
       `commercial`.`name2` AS `t7_c2`,
       `commercial`.`name_on_invoice` AS `t7_c3`,
       `commercial`.`organization_type` AS `t7_c4`,
       `organizations`.`id` AS `t8_c0`,
       `organizations`.`person_id` AS `t8_c1`,
       `organizations`.`address_id` AS `t8_c2`,
       `organizations`.`status` AS `t8_c3`,
       `shaddresses`.`id` AS `t9_c0`,
       `shaddresses`.`zipcode` AS `t9_c1`,
       `shaddresses`.`country` AS `t9_c2`,
       `shaddresses`.`city` AS `t9_c3`,
       `shprivate`.`id` AS `t10_c0`,
       `shprivate`.`first_name` AS `t10_c1`,
       `shprivate`.`last_name` AS `t10_c2`,
       `coraddresses`.`id` AS `t11_c0`,
       `coraddresses`.`zipcode` AS `t11_c1`,
       `coraddresses`.`country` AS `t11_c2`,
       `corprivate`.`id` AS `t12_c0`,
       `corprivate`.`first_name` AS `t12_c1`,
       `corprivate`.`last_name` AS `t12_c2`,
FROM `client` `client`
LEFT OUTER JOIN `users` `users` ON (`client`.`user_id`=`users`.`id`)
AND (users.status!=5)
LEFT OUTER JOIN `private` `privateData` ON (`users`.`person_id`=`privateData`.`id`)
LEFT OUTER JOIN `tariff` `tariff` ON (`client`.`rate_id`=`tariff`.`id`)
LEFT OUTER JOIN `providers` `providers` ON (`client`.`provider_id`=`providers`.`id`)
LEFT OUTER JOIN `addresses` `addresses` ON (`client`.`main_address_id`=`addresses`.`id`)
LEFT OUTER JOIN `private` `private` ON (`client`.`main_person_id`=`private`.`id`)
LEFT OUTER JOIN `commercial` `commercial` ON (`client`.`main_organization_id`=`commercial`.`id`)
LEFT OUTER JOIN `organizations` `organizations` ON (`client`.`id_organization`=`organizations`.`id`)
AND (organizations.status!=5)
LEFT OUTER JOIN `addresses` `shaddresses` ON (`client`.`shipping_address_id`=`shaddresses`.`id`)
LEFT OUTER JOIN `private` `shprivate` ON (`client`.`shipping_person_id`=`shprivate`.`id`)
LEFT OUTER JOIN `addresses` `coraddresses` ON (`client`.`correspondense_address_id`=`coraddresses`.`id`)
LEFT OUTER JOIN `private` `corprivate` ON (`client`.`correspondense_person_id`=`corprivate`.`id`)
WHERE (client.status!=5)
ORDER BY client.id DESC
LIMIT 10
OFFSET 10

Я могу изменить любой ИНДЕКС, но я не могу изменить запрос. На старом хосте он работал за 0,2 секунды, но оптимизатор использовал индексы из таблицы клиентов. С Percona Server (mysql 5.7) это занимает 15 секунд. Оптимизатор не использует какой-либо индекс из таблицы клиентов. С FORCE INCEX () из таблицы клиентов происходит меньше 1 секунды (с составным индексом - около 0,2 секунды). Таблица «Провайдеры» имеет только 1 строку. Я установил индексы для таблицы «клиенты», но в объяснении они не отображаются в качестве возможных ключей.

Я попытался установить для переменной MySql 'max_seeks_for_key' значение 1, но она по-прежнему не использует индексы.

Я думаю, что мне не хватает чего-то простого, но я не могу понять, что.

ОБЪЯСНЕНИЕ для этого запроса:

enter image description here

ORDER BY генерирует TEMPORARY TABLE, который использует все ресурсы (без order by выполняется менее чем за одну секунду, даже без INDEX).

Любая идея ценится.

Ответы [ 4 ]

0 голосов
/ 28 мая 2018

Полезно ли использовать индекс для запросов ORDER BY ... LIMIT, зависит от селективности условий в таблице.Если большинство строк не удовлетворяет условию, может быть быстрее использовать сканирование таблицы, чем для сканирования больших частей индекса.

Следовательно, если оценка селективности выключена, оптимизатор запросов может выбратьнеоптимальный план.Для этого запроса, если в столбце состояния нет индекса, оценка будет неточной, поскольку оптимизатор не будет иметь никакой статистики о распределении этого столбца.Чтобы получить более точные оценки, вы можете попытаться создать индекс для этого столбца.Другой вариант - отключить использование фильтрации оценок, которая была представлена ​​в MySQL 5.7.Для этого выполните:

SET optimizer_switch='condition_fanout_filter=off'

Однако, даже если оценка селективности условия верна, оптимизатору может не удастся выбрать оптимальный план, если столбцы коррелированы.Я подозреваю, что в вашем случае может быть высокая корреляция между статусом и идентификатором.Возможно, в целом очень мало строк, в которых статус! = 5, но это довольно часто среди строк с высокими идентификаторами.Если это так, то я боюсь, что оптимизатор запросов не сможет обнаружить это, и единственный способ получить наиболее оптимальный план - использовать подсказку индекса.

Если вы можете предоставить оптимизаторПроследив за этим запросом, я бы смог больше понять, что происходит.См. https://oysteing.blogspot.no/2016/01/how-to-get-optimizer-trace-for-query.html для получения совета о том, как получить трассировку оптимизатора.

0 голосов
/ 20 мая 2018

Это должно быть более эффективно, чем даже с FORCE:

SELECT  ...
    FROM ( SELECT  id
              FROM  client
              WHERE  status != 5
              ORDER BY  id DESC
              LIMIT  10 OFFSET 10 
          ) AS ids
    JOIN  client USING(id)
    LEFT OUTER JOIN  `users` `users`  ON (`client`.`user_id`=`users`.`id`)
      AND  (users.status!=5)
    LEFT OUTER JOIN  `private` `privateData`  ON (`users`.`person_id`=`privateData`.`id`)
    LEFT OUTER JOIN  `tariff` `tariff`  ON (`client`.`rate_id`=`tariff`.`id`)
    LEFT OUTER JOIN  `providers` `providers`  ON (`client`.`provider_id`=`providers`.`id`)
    LEFT OUTER JOIN  `addresses` `addresses`  ON (`client`.`main_address_id`=`addresses`.`id`)
    LEFT OUTER JOIN  `private` `private`  ON (`client`.`main_person_id`=`private`.`id`)
    LEFT OUTER JOIN  `commercial` `commercial`  ON (`client`.`main_organization_id`=`commercial`.`id`)
    LEFT OUTER JOIN  `organizations` `organizations`  ON (`client`.`id_organization`=`organizations`.`id`)
      AND  (organizations.status!=5)
    LEFT OUTER JOIN  `addresses` `shaddresses`  ON (`client`.`shipping_address_id`=`shaddresses`.`id`)
    LEFT OUTER JOIN  `private` `shprivate`  ON (`client`.`shipping_person_id`=`shprivate`.`id`)
    LEFT OUTER JOIN  `addresses` `coraddresses`  ON (`client`.`correspondense_address_id`=`coraddresses`.`id`)
    LEFT OUTER JOIN  `private` `corprivate`  ON (`client`.`correspondense_person_id`=`corprivate`.`id`)
    ORDER BY  client.id DESC -- Yes, repeated here 
0 голосов
/ 20 мая 2018

MySQL должен иметь возможность использовать индекс в предложении ORDER BY для быстрого отслеживания записей, требуемых из таблицы client. Оттуда объединение нескольких строк с другими таблицами для получения дополнительной информации должно быть быстрым.

Попробуйте добавить этот индекс:

ALTER TABLE `client` ADD INDEX `client_idx_id` (`id`);

Если MySQL решает не использовать этот индекс (иногда он принимает неправильное решение), попробуйте принудительно применить его и сравните продолжительность выполнения этой опции.

0 голосов
/ 14 мая 2018

Я не нашел никакого решения и просто использовал ... FORCE INDEX (статус).

...