Плохая производительность запросов. Где мне нужно создавать индексы? - PullRequest
0 голосов
/ 10 января 2019

У меня относительно простой запрос, но его производительность действительно плохая.

Сейчас я нахожусь примерно на 0,800 сек. за запрос.

Есть ли что-нибудь, что я мог бы изменить, чтобы сделать это быстрее?

Я уже пробовал индексировать столбцы, используемые в операторе where и join, но ничего не работает.

Вот запрос, который я использую:

SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE (el1.artigoE IN (342197) OR el1.artigoD IN (342197))
AND el1.desmembrado = 1 

Вот ОБЪЯСНЕНИЕ: enter image description here

Как спросил Билл Карвин, ниже следует запрос, использованный для создания таблицы:

Таблица "encomendas_c_linhas" https://pastebin.com/73WcsDnE

Таблица "encomendas_c"

https://pastebin.com/yCUx3wh0

Ответы [ 2 ]

0 голосов
/ 10 января 2019

В вашем EXPLAIN мы видим, что он обращается к таблице el1 с type: ALL, что означает, что он сканирует всю таблицу. Поле rows показывает оценку 644 236 отсканированных строк (это приблизительно).

В вашем определении таблицы для таблицы el1 у вас есть этот индекс:

  KEY `order_item_id_desmembrado` (`order_item_id`,`desmembrado`),

Даже если desmembrado присутствует в индексе, этот индекс не поможет в этом запросе. Ваш запрос ищет desmembrado = 1, без условий для order_item_id.

Подумайте о телефонной книге: я могу искать людей с фамилией «Смит», и порядок телефонной книги помогает мне быстро их найти. Но если я ищу людей с именем «Сара», книга не поможет. Только если я буду искать по крайнему левому столбцу (ам) индекса, это поможет.

Итак, вам нужен индекс с desmembrado в качестве крайнего левого столбца . Тогда поиск desmembrado = 1 может использовать индекс для выбора соответствующих строк.

ALTER TABLE encomendas_c_linhas ADD INDEX (desmembrado);

Обратите внимание, что если совпадает достаточно большая часть таблицы, MySQL все равно пропускает этот индекс. Нет никакого преимущества в использовании индекса, если он будет соответствовать большой части строк. По моему опыту, оптимизатор MySQL считает, что он избегает индекса, если условие соответствует> 20% строк таблицы.

Другие условия находятся в дизъюнкте (в выражении OR). Нет способа оптимизировать их с помощью одного индекса. Опять же, пример телефонной книги: Поиск людей с last name 'Smith' OR first name 'Sarah'. Поиск по фамилии оптимизирован, но поиск по имени - нет. Нет проблем, мы можем создать еще один индекс, имя которого будет указано первым, поэтому поиск по имени будет оптимизирован. Но в целом MySQL будет использовать только один индекс для каждой ссылки на таблицу на запрос. Поэтому оптимизация поиска по имени испортит поиск по фамилии.

Вот обходной путь: переписать условие OR в UNION из двух запросов с одним термином в каждом запросе:

SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE el1.artigoD IN ('342197')
AND el1.desmembrado = 1 
UNION
SELECT c.bloqueada, c.nomeF, c.confirmadoData
FROM encomendas_c_linhas el1
LEFT JOIN encomendas_c c ON c.lojaEncomenda=el1.lojaEncomenda
WHERE el1.artigoE IN ('342197')
AND el1.desmembrado = 1;

Убедитесь, что для каждого случая есть индекс.

ALTER TABLE encomendas_c_linhas 
  ADD INDEX des_artigoD (desmembrado, artigoD),
  ADD INDEX des_artigoE (desmembrado, artigoE);

Каждый из этих составных индексов может использоваться в соответствующем подзапросе, поэтому он оптимизирует поиск двух столбцов в каждом случае.

Также обратите внимание, что я поместил значения в кавычки, например IN ('342197'), потому что столбцы varchar, и вам нужно сравнить с varchar, чтобы использовать индекс. Сравнение столбца varchar с целочисленным значением приведет к успешному совпадению, но индекс не будет использоваться.

Вот ОБЪЯСНЕНИЕ, которое я протестировал для предыдущего запроса, которое показывает, что используются два новых индекса, и это показывает ref: const,const, что означает, что оба столбца индекса используются для поиска.

+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+
| id | select_type  | table      | type | possible_keys           | key           | key_len | ref                    | rows | Extra                 |
+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+
|  1 | PRIMARY      | el1        | ref  | des_artigoD,des_artigoE | des_artigoD   | 41      | const,const            |    1 | Using index condition |
|  1 | PRIMARY      | c          | ref  | lojaEncomenda           | lojaEncomenda | 61      | test.el1.lojaEncomenda |    2 | NULL                  |
|  2 | UNION        | el1        | ref  | des_artigoD,des_artigoE | des_artigoE   | 41      | const,const            |    1 | Using index condition |
|  2 | UNION        | c          | ref  | lojaEncomenda           | lojaEncomenda | 61      | test.el1.lojaEncomenda |    2 | NULL                  |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL                    | NULL          | NULL    | NULL                   | NULL | Using temporary       |
+----+--------------+------------+------+-------------------------+---------------+---------+------------------------+------+-----------------------+

Но мы можем сделать один шаг лучше. Иногда полезно добавить другие столбцы в индекс, потому что, если все столбцы, необходимые для запроса, включены в индекс, тогда ему вообще не нужно читать строки таблицы. Это называется индексом покрытия и указывается, если вы видите «Использование индекса» в поле EXPLAIN Extra.

Итак, вот новое определение индекса:

ALTER TABLE encomendas_c_linhas 
  ADD INDEX des_artigoD (desmembrado, artigoD, lojaEncomenda),
  ADD INDEX des_artigoE (desmembrado, artigoE, lojaEncomenda);

Третий столбец не используется для поиска, но используется при соединении с другой таблицей c.

Вы также можете получить тот же эффект индекса покрытия, создав второй индекс, предложенный в ответе @TheImpaler:

create index ix2 on encomendas_c (lojaEncomenda, bloqueada, nomeF, confirmadoData);

Мы видим, что EXPLAIN теперь показывает примечание «Использование индекса» для всех ссылок на таблицы:

+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+
| id | select_type  | table      | type | possible_keys           | key         | key_len | ref                    | rows | Extra                    |
+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+
|  1 | PRIMARY      | el1        | ref  | des_artigoD,des_artigoE | des_artigoD | 41      | const,const            |    1 | Using where; Using index |
|  1 | PRIMARY      | c          | ref  | lojaEncomenda,ix2       | ix2         | 61      | test.el1.lojaEncomenda |    1 | Using index              |
|  2 | UNION        | el1        | ref  | des_artigoD,des_artigoE | des_artigoE | 41      | const,const            |    1 | Using where; Using index |
|  2 | UNION        | c          | ref  | lojaEncomenda,ix2       | ix2         | 61      | test.el1.lojaEncomenda |    1 | Using index              |
| NULL | UNION RESULT | <union1,2> | ALL  | NULL                    | NULL        | NULL    | NULL                   | NULL | Using temporary          |
+----+--------------+------------+------+-------------------------+-------------+---------+------------------------+------+--------------------------+
0 голосов
/ 10 января 2019

Я бы создал следующие индексы:

create index ix1 on encomendas_c_linhas (artigoE, artigoD, desmembrado);

create index ix2 on encomendas_c (lojaEncomenda, bloqueada, nomeF, confirmadoData);

Первый является критическим. Второй еще улучшит производительность.

...