Каждый из нас, наверняка, сталкивался с ситуацией, когда имея два поля в условии запроса и два индекса по этим полям выборка производится довольно медленно:
что несколько странно, так как индексы на field1 и field2 у нас в наличии, а id так вообще первичный ключ, но даже если без сортировки, что все равно не все проходит по индексу:
легче, но не намного, так как фильтр по field2 в данном примере накладывается всего на 28 тысяч записей, да сортировка таки нужна :-(
Для этого существуют композитные индексы, которые, собственно, позволяют использовать их для множественных условий:
Увеличение производительности видно даже на таком небольшом количестве записей, но при добавлении сортировки в запрос, легче совсем не становится:
все потому, что сортировка так же является одним из условий запроса, соответственно поле сортировки так же нужно добавлять в композитный индекс:
вуаля!
Что интересно, при этом анализатор не говорит, что сортировка производится тоже по индексу, но при этом сортирует именно по нему.
Теперь можно поговорить уже и про теорию, что же это за композитные индексы такие...
Как это работает
Композитный индекс можно представить в виде такой схемы:
Как видно из схемы, что для каждого из элементов на следующем уровне формируется частичный индекс в его (элемента) пределах. Отсюда становится понятным, что порядок полей в композитном индексе имеет очень большое значение.
Так, в предыдущем примере (SQL код (5)) мы указали последовательность полей как (field1, field2, id), можно сравнить, что будет происходить, если мы поменяем порядок полей как (id, field1, field2):
Как видно, скорость упала в разы. Рассмотрим как производится выборка по индексу в обоих случаях:
(field1, field2, id)
(id, field1, field2)
Теперь понятно откуда такие задержки. По сути получается все равно фильтрация, но по индексу, так как данные для этого присутствуют. Так же видно, что уникальное поле в композитном индексе имеет смысл ставить только в конце, так как на следующих уровнях индекса буджет находится всего по одному элементу.
Немного усложним задачу и уберем LIMIT:
Как видно, при больших объемах, разница незначительная, так как все начинает упираться в чтение строк с диска.
Как это использовать
Итак, как работают композитные индексы, понятно. Остается вопрос, как правильно их использовать.
Первое на что хотел бы обратить внимание, опять же порядок. Понятно, что в первую очередь в композитном индексе нужно указывать поля из условия WHERE, а во второй - из сортировки ORDER BY. Причем, если порядок полей из сортировки понятен - ровно такой, какой указываем в сортировке, то порядок полей из условия WHERE не совсем однозначен.
По сути, для получения данных порядок полей в композитном индексе не имеет значения, но для планировщика запросов очень даже имеет, так например:
Казалось бы, совершенно неожиданное поведение, у нас же есть специально заточненный индекс для этого запроса, а он, почему-то использует другой индекс, причем, если мы не создадим индекс field1_idx, то все становится в норму:
Все очень просто. Вариантов значений field1 - 1269, а field2 всего 23:
В итоге планировщик принимает относительно правильное решение, отфильтровывать сначала 1/1269 часть данных, чем 1/23.
Отсюда правило: порядок полей из условия WHERE зависит от того, сколько вариантов значений может принимать поле, чем больше - тем первее.
Как это можно оптимизировать
Понятно, что чем больше полей мы укажем в композитном индексе, тем его размер будет больше, поэтому увлекаться ими сильно не стоит.
Что бы уменьшить объем можно сделать разделенный композитный индекс. Так например:
Есть каталог новостей, новость привязана к рубрике, у новости есть статус (черновик, показывать в рубрике, архивная новость):
Соответственно можно создать композитный индекс:
Но, в списочном контексте показываются только новости со статусом "показывать в рубрике", соответственно, черновики и архив нам не нужен в индексе, поэтому его разделяем:
Что значительно облегчит индекс. Причем поле status вообще убирается из индекса, так как фильтрация по этому полю будет производится на уровне разделителя индекса, правда, если нам понадобится несколько статусов, то это поле все же придется добавить в индекс.
Итоги
В итоге получились следующие заключения для композитный индексов:
Использование композитных индексов значительно ускоряет работу базы данных на сложных условиях;
Уникальные поля желательно ставить в конце композитного индекса;
В первую очередь идут поля из условия WHERE, потом из сортировки ORDER BY;
Поля из условия WHERE указывать в порядке - сначала поля с большими вариантами принимаемых значений, потом с меньшими;
Поля из сортировки ORDER BY - в соотвествии с порядком сортировки;
По возможности делать разделенные индексы, то есть в индексе должны быть только те данные, которые мы будем реально выбирать;
P.S.
Кстати композитные бывают не только индексы и сортировки, а еще и условия. Так например запрос:
Выберет следующие 10 записей после записи с id = 1234 в соответсвии с композитным порядком сортировки. Причем даже если у последующих записей поле rating такое же, но дата меньше, они будут все равно выбраны, так как у нас условие ( i.rating, i.date ) < ... композитное. Правда, при разнонаправленной сортировке такой фокус не пройдет.