Mysql плохой план исполнения - PullRequest
0 голосов
/ 07 сентября 2018

Мне нужна помощь в понимании вывода данных путем объяснения очень похожих запросов и огромного влияния на производительность. У меня есть 2 таблицы: аннонс и геолокализация. Первый содержит объявления об аренде, а второй - соответствующее местоположение. Итак, мы ищем аренду в данном месте. Если я использую план по умолчанию

EXPLAIN
SELECT a.*, g.label AS geo_label, g.geo_url
FROM annonce a
INNER JOIN geolocalisation g ON a.geolocalisation_id = g.geolocalisation_id 
WHERE a.categorie_id = 1 AND g.gauche >= 151579 AND g.droite <= 151580 
AND couchage >= 2
ORDER BY FIELD(provenance_id, 2, 1), prix DESC, date_modification DESC, annonce_id ASC

У меня время выполнения более 10 с.

+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                   | key          | key_len | ref                          | rows   | filtered | Extra                                              |
+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | a     | NULL       | ref    | geolocalisation_id,categorie_id | categorie_id | 4       | const                        | 502897 |    33.33 | Using index condition; Using where; Using filesort |
|  1 | SIMPLE      | g     | NULL       | eq_ref | PRIMARY,droite,gauche           | PRIMARY      | 4       | vacamax.a.geolocalisation_id |      1 |    25.00 | Using where                                        |
+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+

Если я заставлю индекс геолокации "гаучить"

EXPLAIN
SELECT a.*, g.label AS geo_label, g.geo_url
FROM annonce a
INNER JOIN geolocalisation g ON a.geolocalisation_id = g.geolocalisation_id 
WHERE a.categorie_id = 1 AND g.gauche >= 151579 AND g.droite <= 151580 
AND couchage >= 2
ORDER BY FIELD(provenance_id, 2, 1), prix DESC, date_modification DESC, annonce_id ASC

У меня есть время выполнения .1с

+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                   | key                | key_len | ref                          | rows  | filtered | Extra                                                               |
+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+
|  1 | SIMPLE      | g     | NULL       | range | gauche                          | gauche             | 4       | NULL                         | 52785 |    33.33 | Using index condition; Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | a     | NULL       | ref   | geolocalisation_id,categorie_id | geolocalisation_id | 5       | vacamax.g.geolocalisation_id |    13 |    16.66 | Using where                                                         |
+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+

Результат - 188 строк. Кажется, что в первом случае тестируется слишком много строк, но во втором случае фильтрация эффективна: геолокализация - это фильтр, который следует применять ДО объединения: 1) вы получаете места, удовлетворяющие условиям 2) вы найдете аренду с такими размещает geolocalisation_id путем сопоставления таблиц. Пожалуйста, просветите меня.

1 Ответ

0 голосов
/ 07 сентября 2018

Вы знаете, что фильтрацию геолокации разумнее выполнять до, чем после, потому что вы знаете что-то о своих данных и своем запросе, чего нет в MySQL.

В частности, MySQL предполагает, что ему нужно просмотреть 502897*1 строк в первом запросе, и 52785*13=686205 строк для второго запроса, и решает использовать первый. Есть и другие факторы, которые влияют на решение о том, какой план выполнения использовать, но он дает вам приблизительное представление о том, как MySQL считает ваши данные. Это далеко от реальности (188 строк), и не удивительно, что принятие решения на основе таких неправильных предположений привело к плохой стратегии.

На самом деле, даже я знаю, что только потому, что вы сказали мне, и теперь можете предположить, основываясь на именах столбцов, что gauche всегда меньше, чем droite, поэтому ваше условие для g, вероятно, описывает очень узкое окно. Но MySQL этого не знает, поскольку вы не сказали об этом MySQL, поэтому он не может принять это во внимание. И, конечно же, он не способен принимать решения на основании значения имен столбцов.

Поскольку у вас есть индекс на gauge, для высокого значения (например, g.gauge >= your_max_value_in_that_column) MySQL должен действительно быть в состоянии обнаружить, что имеется только несколько строк, и должен использовать лучший план выполнения. В противном случае MySQL в основном не имеет понятия. Попробуйте изменить размер окна в очень широком диапазоне (например, g.gauche >= 100000 AND g.droite <= 200000); MySQL не покажет существенно другое число в rows, если вы не приблизитесь к пределам своих столбцов (и не будете иметь индекс для них). Для некоторых диапазонов первый запрос на самом деле должен быть быстрее, поскольку он приближается к распределению данных, которое предполагает MySQL.

Так как вы можете рассказать MySQL о вашем распределении данных?

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

Предполагая, что мои предположения верны, вы также можете использовать (g.gauche = 151579 or g.gauche = 151580), и MySQL также должен понимать, что это только ограниченный объем данных.

И вы, конечно, можете просто принудительно индексировать (или использовать FROM geolocalisation g STRAIGHT_JOIN annonce a). Вы знаете, что MySQL не знает, и часто вы не можете сказать MySQL иначе. Недостатком является то, что это не может быть адаптировано к другим ситуациям, например если вы (иногда) используете большие окна в своем запросе, или gauche <= droite больше не соответствует действительности.

...