Почему оператор select влияет на выполнение и производительность запроса в MySQL?

0

Я встречаю странное поведение MySQL. Выполнение запроса (т.е. Использование индексов, как показано в пояснении [QUERY]), и время, необходимое для выполнения, зависят от элементов предложения where.

Вот запрос, в котором возникает проблема:

select distinct
e1.idx, el1.idx, r1.fk_cat, r2.fk_cat
from ent e1, ent_leng el1, rel_c r1, _tax_c t1, rel_c r2, _tax_c t2
where el1.fk_ent=e1.idx
and r1.fk_ent=e1.idx and ((r1.fk_cat=43) or (r1.fk_cat=t1.fk_cat1 and t1.fk_cat2=43))
and r2.fk_ent=e1.idx and ((r2.fk_cat=10) or (r2.fk_cat=t2.fk_cat1 and t2.fk_cat2=10))

Соответствующий вывод объяснения:

| id | select_type | table | type   | possible_keys           | key     | key_len | ref           | rows  | Extra                       
+----+-------------+-------+--------+-------------------------+---------+---------+---------------+-------+------------------------------------
|  1 | SIMPLE      | el1   | index  | fk_ent                  | fk_ent  | 4       | NULL          | 15002 | Using index; Using temporary
|  1 | SIMPLE      | e1    | eq_ref | PRIMARY                 | PRIMARY | 4       | DB.el1.fk_ent |     1 | Using index
|  1 | SIMPLE      | r1    | ref    | fk_ent,fk_cat,fks       | fks     | 4       | DB.e1.idx     |     1 | Using where; Using index
|  1 | SIMPLE      | r2    | ref    | fk_ent,fk_cat,fks       | fks     | 4       | DB.el1.fk_ent |     1 | Using index
|  1 | SIMPLE      | t1    | index  | fk_cat1,fk_cat2,fk_cats | fk_cats | 8       | NULL          |    69 | Using where; Using index; Distinct; 
|    |             |       |        |                         |         |         |               |       | Using join buffer
|  1 | SIMPLE      | t2    | index  | fk_cat1,fk_cat2,fk_cats | fk_cats | 8       | NULL          |    69 | Using where; Using index; Distinct;  
                                                                                                          | Using join buffer

Как вы видите, индекс с одним столбцом имеет то же имя, что и столбец, которому он принадлежит. Я также добавил некоторые бесполезные индексы вместе с используемыми, просто чтобы проверить, не изменили ли они выполнение (чего у них нет).

Выполнение занимает ~ 4,5 секунды.

Когда я добавляю столбец entl1.name в часть выбора (ничего больше не изменил), индекс fk_ent в el1 больше не может быть использован:

| id | select_type | table | type   | possible_keys           | key     | key_len | ref           | rows  | Extra                       
+----+-------------+-------+--------+-------------------------+---------+---------+---------------+-------+------------------------------------
|  1 | SIMPLE      | el1   | ALL    | fk_ent                  |  NULL   | NULL    | NULL          | 15002 | Using temporary

Выполнение теперь занимает ~ 8.5 секунд.

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

Оставить атрибут не является решением, и есть еще больше атрибутов, которые я должен выбрать. Хуже того, запрос в используемой форме даже немного сложнее, и проблема с производительностью является большой проблемой.

Итак, мои вопросы: 1) В чем причина этого странного поведения? 2) Как я могу решить проблему с производительностью?

Спасибо за вашу помощь! GRED

Теги:
indexing
query-performance

2 ответа

2
Лучший ответ

Это ограничение DISTINCT. Вы можете думать об этом как о другом запрете WHERE. Когда вы меняете список выбора, вы действительно меняете предложение WHERE для ограничения DISTINCT, и теперь оптимизатор решает, что он все равно должен выполнять сканирование таблицы, поэтому он может также не использовать ваш индекс.

EDIT:

Не уверен, что это помогает, но если я правильно понимаю ваши данные, я думаю, вы можете избавиться от ограничения DISTINCT следующим образом:

select
e1.idx, el1.idx, r1.fk_cat, r2.fk_cat
from ent e1
  Inner Join ent_leng el1 ON el1.fk_ent=e1.idx
  Inner Join rel_c r1 ON r1.fk_ent=e1.idx
  Inner Join rel_c r2 ON r2.fk_ent=e1.idx
where 
 ((r1.fk_cat=43) or Exists(Select 1 From _tax_c t1 Where r1.fk_cat=t1.fk_cat1 and t1.fk_cat2=43)) 
 and 
 ((r2.fk_cat=10) or Exists(Select 1 From _tax_c t2 Where r2.fk_cat=t2.fk_cat1 and t2.fk_cat2=10))
  • 0
    Звучит разумно. Но это не решение проблемы (я должен был бы иметь дело с огромным, почти квази избыточным набором результатов или использовать сложный индекс). И, как ни странно, удаление DISTINCT не изменяет вывод EXPLAIN и не ускоряет запрос, как я только что проверил.
  • 0
    Только что проверил: Ваш запрос, похоже, дает те же результаты, что и мой, - и это намного быстрее! Спасибо! Я думаю, что мне нужно глубже погрузиться в SQL, чтобы использовать его соответствующим образом.
0

MySQL будет возвращать данные из индекса, если это возможно, сохраняя всю загруженную строку. Таким образом, выбранные столбцы могут влиять на выбор индекса.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню