Подтвердить что ты не робот

Является ли порядок полей в предложении WHERE влиять на производительность в MySQL?

У меня есть два индексированных поля в таблице - type и userid (отдельные индексы, а не составные).

type значения поля очень ограничены (допустим, это всего лишь 0 или 1), поэтому 50% записей таблицы имеют одинаковый type. Значения userid, с другой стороны, исходят из гораздо большего набора, поэтому количество записей с одним и тем же userid невелико.

Будет ли любой из этих запросов работать быстрее, чем другой:

select * from table where type=1 and userid=5
select * from table where userid=5 and type=1

Также, если оба поля не были проиндексированы, изменилось бы поведение?

4b9b3361

Ответ 1

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

Я, вероятно, собираюсь упростить следующее обсуждение оптимизатора запросов SQL. Я писал один год назад, в соответствии с этими строками (это было очень весело!). Если вы действительно хотите вникать в современную оптимизацию запросов, см. Dan Tow SQL Tuning от O'Reilly.

В простом оптимизаторе запросов SQL оператор SQL сначала скомпилируется в дерево операций реляционной алгебры. Эти операции каждый принимают одну или несколько таблиц в качестве входных данных и создают другую таблицу в качестве вывода. Сканирование - это последовательное сканирование, которое считывает таблицу из базы данных. Сортировка создает отсортированную таблицу. Выбор создает таблицу, строки которой выбираются из другой таблицы в соответствии с некоторым условием выбора. Проект создает таблицу с определенными столбцами другой таблицы. Cross Product берет две таблицы и создает таблицу вывода, состоящую из всех возможных парных строк.

Смутно, предложение SQL SELECT скомпилировано в проект реляционной алгебры, в то время как предложение WHERE превращается в реляционную алгебру Select. Предложение FROM превращается в одно или несколько соединений, каждый из которых принимает две таблицы и выдает одну таблицу. Существуют и другие операции реляционной алгебры, включающие объединение объединений, пересечение, разность и членство, но пусть это будет просто.

Это дерево действительно нужно оптимизировать. Например, если у вас есть:

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

с 5000 сотрудниками в 500 отделах, выполнение неоптимизированного дерева будет вслепую производить все возможные комбинации одного сотрудника и одного отдела (кросс-продукта), а затем выбрать только одну комбинацию, которая была необходима. Scan of Employee будет составлять 5000 записей, Scan of Department будет составлять 500 записей, Cross Product из этих двух таблиц будет составлять таблицу записей на 2500 000, а Select on E.id займет 2,500,000 записей и отбросить все, кроме одного, запись, которая была нужна.

[Реальные процессоры запросов будут стараться не материализовать все эти промежуточные таблицы в памяти, конечно.]

Таким образом, оптимизатор запросов просматривает дерево и применяет различные оптимизации. Один из них состоит в том, чтобы разбить каждый Select в цепочку Selects, по одному для каждого из исходных условий выбора верхнего уровня, те и другие вместе. (Это называется "конъюнктивной нормальной формой".) Затем отдельные меньшие выборочные перемещения перемещаются в дереве и объединяются с другими операциями реляционной алгебры для формирования более эффективных.

В приведенном выше примере оптимизатор сначала подталкивает Select on E.id = 123456 ниже дорогостоящей операции Cross Product. Это означает, что Cross Product производит только 500 строк (по одному для каждой комбинации этого сотрудника и одного отдела). Затем верхний уровень Select для E.dept_id = D.dept_id отфильтровывает 499 нежелательных строк. Неплохо.

Если есть индекс в поле Employee id, то оптимизатор может объединить Scan of Employee с Select на E.id = 123456, чтобы сформировать быстрый индекс Lookup. Это означает, что только одна строка Employee считывается в память с диска вместо 5000. Вещи смотрят вверх.

Последняя крупная оптимизация - взять Select on E.dept_id = D.dept_id и объединить ее с Cross Product. Это превращает его в операцию реляционной алгебры Equijoin. Это не само по себе. Но если есть индекс на Department.dept_id, то последующий последовательный сканирование Департамента, управляющего Equijoin, может быть превращен в очень быстрый индекс. Поиск нашего отчета сотрудника одного сотрудника.

Малая оптимизация включает в себя толкание операций проекта вниз. Если для верхнего уровня вашего запроса просто нужны имена E. и D., и для этого нужны условия E.id, E.dept_id и D.dept_id, тогда для операций сканирования не нужно создавать промежуточные таблицы со всеми остальными столбцы, экономя пространство во время выполнения запроса. Мы превратили ужасно медленный запрос в два индекса поиска и не намного больше.

Чтобы узнать больше об исходном вопросе, скажем, у вас есть:

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

Неоптимизированное дерево реляционной алгебры при его выполнении будет сканировать у 5000 сотрудников и произвести, скажем, 126 в Делавэре, которые старше 21. Оптимизатор запросов также имеет некоторое приблизительное представление о значениях в базе данных. Он может знать, что столбец E.state имеет 14 состояний, в которых расположены компании, и что-то о дистрибутивах E.age. Поэтому сначала он видит, проиндексировано ли какое-либо поле. Если существует E.state, имеет смысл использовать этот индекс, чтобы просто выбрать небольшое количество сотрудников, которые подозреваемые в процессоре запросов находятся в штате Делавэр на основе его последней вычисленной статистики. Если только E.age, процессор запросов, вероятно, решит, что он этого не стоит, поскольку 96% всех сотрудников - 22 и старше. Поэтому, если E.state индексируется, наш процессор запросов разбивает Select и объединяет E.state = 'Delaware' с Scan, чтобы превратить его в гораздо более эффективный индексный сканер.

В этом примере скажем, что на E.state и E.age нет индексов. Объединенная операция выбора выполняется после последовательного "сканирования" сотрудника. Неважно, какое условие в Select выполняется в первую очередь? Наверное, не так много. Обработчик запросов может оставить их в исходном порядке в инструкции SQL, или он может быть немного сложнее и посмотреть на ожидаемый расход. По статистике, он снова обнаружил бы, что условие E.state = 'Delaware' должно быть более избирательным, поэтому оно будет отменять условия и делать это в первую очередь, так что будет только 126 сравнений E.age > 21 вместо 5000, Или он может понять, что сравнение равенства строк намного дороже, чем целочисленное сравнение и оставить заказ один.

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

Ответ 2

Это не должно быть в вашем маленьком примере. Оптимизатор запросов должен делать все правильно. Вы можете убедиться, добавив explain в начало запроса. MySQL расскажет вам, как он объединяет вещи и сколько строк нужно искать, чтобы выполнить соединение. Например:

explain select * from table where type=1 and userid=5

Если они не были проиндексированы, это, вероятно, изменило бы поведение.

Ответ 3

Большинство оптимизаторов запросов используют порядок, в котором условия отображаются как подсказка. Если все остальное равно, они будут следовать этому порядку.

Однако многие вещи могут переопределить это:

  • второе поле имеет индекс, а первый не имеет
  • есть статистика, чтобы предположить, что поле 2 более избирательно
  • второе поле легче искать (varchar(max) vs int)

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