Почему оператор select влияет на выполнение и производительность запроса в MySQL? - PullRequest
0 голосов
/ 22 сентября 2010

Я сталкиваюсь со странным поведением 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 секунд.

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

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

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

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

Ответы [ 2 ]

2 голосов
/ 22 сентября 2010

Это ОТЛИЧНОЕ ограничение.Вы можете думать об этом как о другом ограничении ГДЕ.Когда вы изменяете список выбора, вы действительно меняете предложение WHERE для ограничения DISTINCT, и теперь оптимизатор решает, что ему все равно нужно выполнить сканирование таблицы, поэтому он может также не использовать ваш индекс.

РЕДАКТИРОВАТЬ:

Не уверен, что это поможет, но если я правильно понимаю ваши данные, я думаю, вы можете избавиться от ограничения 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 голосов
/ 30 октября 2013

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

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

...