Ускорение предложения ORDER BY с индексом

У меня есть запрос с предложением ORDER BY, который выполняется медленно из-за того, что в таблице более 11 миллионов строк.

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

У меня вопрос: можно ли использовать некластеризованные индексы для улучшения ORDER BY производительности? Или в кластерных индексах есть что-то особенное, что означает, что я не смогу быстро выполнить сортировку по всем столбцам?

Примечание. Я разместил свой настоящий план запроса и выполнения в Интернете, но есть и другие вопросы, о которых я не хочу здесь вдаваться. Я не создавал базу данных и не писал запрос. И запрос по-прежнему очень медленный даже без предложения IN.


person Jonathan Wood    schedule 24.08.2018    source источник
comment
Это ОЧЕНЬ СТРАННЫЙ вопрос от человека, который набрал более 41 тыс. Точек и имеет дело с более чем 11 миллионами строк в таблице. Прямой ответ на вопрос can non-clustered indexes be used to improve ORDER BY performance? - да. Но, не имея на сервере реальных планов выполнения, таблиц DDL + DML и всей информации для воспроизведения проблемы, мы можем только сказать, что это возможно, но не обязательно.   -  person Ronen Ariely    schedule 24.08.2018
comment
@RonenAriely: Если не вдаваться в подробности, есть ли причина, по которой мы не можем говорить об общих правилах того, когда индекс может улучшить производительность сортировки? (Я задал связанный вопрос с упрощенным запросом, и мне было предложено опубликовать свой настоящий запрос, в котором много чего происходило, а затем мне пришлось иметь дело с обсуждениями частей исходного запроса, которые не имели ничего общего с моей проблемой. Почему мы не можем говорить об индексах и производительности сортировки?)   -  person Jonathan Wood    schedule 24.08.2018
comment
Потому что нет правила, которое гласит, что в ситуации X некластеризованный индекс повысит производительность. Буквально существуют книги и книги об индексах и о том, как они связаны с производительностью. Хорошая стратегия индексации - это примерно 60% технического мастерства и 40% искусства, полученного как в результате опыта, так и в результате тестирования.   -  person Sean Lange    schedule 24.08.2018
comment
Как вы думаете, кластерный индекс - это то, что вы даете нам ссылку, чтобы показать, что может быть только один. Это базовые знания. Как вы думаете, что такое некластеризованный индекс, если вы не уверены, что его можно использовать для повышения производительности сортировки (в определенных случаях)? Я рекомендую найти хорошее онлайн-руководство и прочитать об индексах и о том, почему мы их используем. Вкратце: кластеризованный индекс - это физический порядок строк в таблице (поэтому как у вас может быть больше одной?!?). Некластеризованный индекс - это отдельный элемент от сказки, в котором хранятся данные (например, в древовидной структуре).   -  person Ronen Ariely    schedule 24.08.2018
comment
Отметьте этот документ как отправную точку: docs.microsoft.com/en-us/sql/relational-databases/indexes/   -  person Ronen Ariely    schedule 24.08.2018
comment
@RonenAriely: Я знаком с ограничением только одного кластерного индекса, поэтому я сказал, что в мой вопрос можно добавить только один. Я просматривал эту ссылку, но, похоже, в моем понимании все еще есть дыра.   -  person Jonathan Wood    schedule 24.08.2018
comment
general rules for when an index can improve sort performance: хорошо, мы можем обсудить золотые правила, но они могут работать, а могут и не работать, в чем смысл золотых правил :-). Намного проще понять, что такое некластеризованный индекс и что такое кластерный индекс, что является очень простой темой и является обязательной для всех, кто работает с базами данных. Вы можете попытаться сосредоточиться на конкретном вопросе? Я не уверен, что на данный момент непонятно ;-)   -  person Ronen Ariely    schedule 24.08.2018
comment
МОЖНО ли использовать некластеризованные индексы для улучшения Order By производительности? да. БУДУТ ЛИ некластеризованные индексы улучшить Order By производительность в вашем конкретном случае? Невозможно узнать с помощью той ограниченной информации, которую вы предоставили.   -  person Brian    schedule 24.08.2018
comment
@RonenAriely: Во-первых, мой настоящий опыт - это C / C ++ / C # и тому подобное. Я просто делаю то, что мне нужно, в SQL. Во-вторых, я опубликовал связанный вопрос, и мне было предложено добавить кластерный индекс для ускорения сортировки. Но теперь мой вопрос в том, как сделать то же самое, если мне может понадобиться отсортировать один из нескольких разных столбцов. Вот в чем проблема. И я добавил ссылку на свой исходный запрос и план выполнения. Пожалуйста, не поднимайте проблем с предложением IN.   -  person Jonathan Wood    schedule 24.08.2018
comment
@Brian: Теперь этот комментарий был опубликован после, я добавил ссылку на свой исходный запрос и план выполнения.   -  person Jonathan Wood    schedule 24.08.2018
comment
First off, my real expertise is C/C++/C#: Хорошо, в этом есть смысл :-), но базой данных, как вы описали, обычно управляет администратор базы данных. Во всяком случае, давайте попробуем помочь вам на стороне базы данных   -  person Ronen Ariely    schedule 24.08.2018
comment
Возможно, составной некластеризованный индекс поможет на (lprOwner, lprDate)? И аналогичный индекс для (lprOwner, X), где X - это каждый столбец, по которому этот запрос может сортировать. Имейте в виду, что хранить и поддерживать индексы дорого, поэтому, если пользователи могут сортировать по 2 или 3 столбцам, это нормально, но если они могут сортировать по любому столбцу в таблице, это может быть плохим решением.   -  person Brandon    schedule 24.08.2018


Ответы (3)


Некластеризованные индексы можно абсолютно использовать для оптимизации сортировки. Индексы - это, по сути, бинарные деревья поиска, что означает, что они содержат значения, отсортированные по порядку.

Однако, в зависимости от запроса, вы можете поставить SQL Server в затруднительное положение.

Если у вас есть таблица со 100 миллионами строк, ваш запрос будет соответствовать 11 миллионам из них, как показано ниже: дешевле ли использовать индекс на category для выбора строк и сортировки результатов по name или для чтения всех 100 миллионов строк индекса, предварительно отсортированного по name, а затем отфильтруйте 89 миллионов из них, установив флажок category?

select ...
from product
where category = ?
order by name;

Теоретически SQL Server может использовать индекс name для чтения строк в порядке и использовать индекс category для эффективной фильтрации? Я настроен скептически. Я редко видел, чтобы SQL Server использовал несколько индексов для доступа к одной и той же таблице в одном запросе (при условии выбора одной таблицы, игнорирования объединений или рекурсивных CTE). Придется проверять индекс 100 миллионов раз. Индексы имеют высокие накладные расходы на поиск по индексу, поэтому они эффективны, когда один поиск значительно сужает набор результатов.

Не видя схемы, статистики и точного запроса, мне трудно сказать, что имеет смысл, но я ожидаю, что я обнаружу, что SQL Server будет использовать индекс для предложения where и сортировать результаты, игнорируя индекс в столбце сортировки.

Индекс в столбце сортировки может использоваться, если вы выбираете всю таблицу. Нравится select ... from product order by name;

Опять же, ваш пробег может отличаться. Это предположение, основанное на прошлом опыте.

person Brandon    schedule 24.08.2018
comment
Спасибо за подробное объяснение. Вы можете просмотреть мой запрос и план выполнения в Интернете, но я не хочу чтобы попасть в предложение IN, поскольку он все равно очень медленный, если я его уберу. - person Jonathan Wood; 24.08.2018
comment
Хорошо, теперь нам есть что обсудить :-), но у нас все еще нет структуры таблицы :-(. Давайте немного обсудим ваш образец: (1) Вы можете видеть, что ваш запрос использует условие lprOwner IN , что означает, что для этого Условие индекса может очень помочь, поскольку его можно использовать для более быстрой фильтрации данных (без структуры вашей таблицы и тестирования запроса с индексами мы не можем быть уверены, поскольку сами данные в таблице могут влиять на план выполнения! для небольшой таблицы сервер может выбрать сканирование таблицы и не использовать индекс, в то время как в другой таблице он может использовать индекс - person Ronen Ariely; 24.08.2018
comment
@RonenAriely: Если я добавлю кластерный индекс, запрос будет почти мгновенным, даже с предложением IN. Так зачем мне делать предложение IN быстрее? (Вот почему я не хотел публиковать свой фактический запрос и постоянно повторял, что не хочу вдаваться в предложение IN.) - person Jonathan Wood; 24.08.2018
comment
Не знаю, нужно ли :-) Я только сказал, что в некоторых случаях может помочь. Я хотел набрать больше очков и начал только с первого раза :-). Во всяком случае, в целом я настоятельно рекомендую не следовать автоматическим рекомендациям, которые вы получаете от инструмента. Вам следует проверить и протестировать каждый вариант. Попробуйте СОЗДАТЬ НЕКЛАСТЕРНЫЙ ИНДЕКС, как рекомендовано в сообщении, и сравните производительность, сравнивая планы выполнения, проверяя статистику ввода-вывода и статистику времени с индексом и без него, и на последнем шаге вы можете создать несколько таблиц и сравнить реальное выполнение на всех в одном время. - person Ronen Ariely; 24.08.2018
comment
Извините, я не могу работать в этом интерфейсе и направлять вас миллионами коротких комментариев без форматированного кода ... Я рекомендую найти хороший учебник (я действительно пытался, но меня слишком раздражает разделять комментарии, а в середине люди меняются их ответы и мой комментарий становятся неуместными, поэтому я начал снова ... это не лучшее место для обучения) - person Ronen Ariely; 24.08.2018
comment
Проблема с этим аргументом в том, что вы просите теоретического совета, но затем отклоняете совет, утверждая, что он неприменим в вашем случае. Удаление предложения IN потенциально меняет ситуацию, поскольку SQL Server имеет различные варианты в качестве своего распоряжения. Смотрите мой ответ выше. Что дешевле: фильтровать с помощью индекса и сортировки или сканировать индекс, чтобы избежать сортировки, а затем фильтровать? Что ж, если убрать предложение where, фильтрации нет. Это меняет вашу ситуацию. - person Brandon; 24.08.2018
comment
If I add a clustered index снова кластерный индекс - это таблица! если вы добавили кластерный индекс, то ваши данные уже отсортированы, поэтому вы получаете быстрый результат. Если вы читаете все данные по порядку кластеризованного индекса, сортировки нет. Но таблица хранит данные один раз, что означает, что она может хранить их только в одном порядке, а это означает, что для этого запроса, возможно, подходит конкретный кластерный индекс, но как насчет вашего следующего запроса?!? Вы должны быть знакомы со всей своей системой, прежде чем создавать структуру таблицы. и помните, что добавление некластеризованного индекса означает, что вы храните больше данных - person Ronen Ariely; 24.08.2018
comment
@Brandon: Я не уверен, что то, что я сказал, не применимо в моем случае. Я понимаю, что вы говорите о том, как предложение IN может повлиять на то, какие индексы могут использоваться для сортировки. Я просто говорю, что добавил кластерный индекс, и запрос выполняется почти мгновенно. Я чувствовал, что нашел решение, пока не узнал, что код иногда приходится сортировать по другим столбцам. Так что я просто пытаюсь понять, как я могу подойти к этой разнице. - person Jonathan Wood; 24.08.2018
comment
Кластерный индекс помогает, потому что путь для выбора строк в вашем плане выполнения - это сканирование таблицы. Кластеризованный индекс на самом деле означает, что ваша таблица хранится в порядке lprDate, поэтому SQL Server может пропустить сортировку. - person Brandon; 24.08.2018
comment
Для продуманного решения вы всегда можете смоделировать преимущества кластерного индекса, создав некластеризованный индекс для каждого столбца, но вам придется включить все столбцы, которые выбираются и фильтруются вашими запросами. Ваш запрос будет молниеносным, но вы в основном сохраните копию таблицы для каждого столбца, который у него есть, то есть 23 копии таблицы. Компромисс между пространством и временем и компромисс между чтением и записью. (Я не предлагаю вам это делать - просто рассказываю, как это работает) - person Brandon; 24.08.2018
comment
@RonenAriely: вы знаете, stackoverflow - это пытаясь побудить людей быть более полезными, а не унижать людей за то, чего они не знают. Я ценю любую предложенную вами помощь, но я не дурак, я создал много полезного программного обеспечения, и в настоящее время у меня нет доступа к администратору баз данных. Мне просто нужна небольшая помощь, и это не подходящий форум для расширенных дискуссий о семантике, ссылках, которые я публикую, насколько странным, по вашему мнению, является мой вопрос. - person Jonathan Wood; 24.08.2018
comment
@Brandon: Да, именно это я и имел в виду. Если я добавлю некластеризованный индекс для каждого столбца, по которому мне может потребоваться сортировка, сможет ли это улучшить производительность так же, как кластерный индекс? Я понимаю, что это замедляет обновления, но мне просто нужно это оценить. - person Jonathan Wood; 24.08.2018
comment
Да, это будет. Но вам, скорее всего, потребуется включить каждый столбец, на который ссылается ваш запрос, в этот индекс. Если вы сделаете это, вы максимизируете производительность запросов для всех видов за счет увеличения дискового пространства в 23 раза и более медленной вставки / обновления / удаления в 23 раза. - person Brandon; 24.08.2018
comment
if I added a non-clustered index for each column: Каждый добавляемый некластеризованный индекс хранится отдельно от данных таблицы (больше данных для чтения и хранения). Каждый добавляемый некластеризованный индекс требует управления, что означает снижение производительности. В целом золотое правило состоит в том, что некластеризованный индекс помогает для запросов SELECT (если это правильный индекс для использования ), но в то же время снижает производительность запросов UPDATE, INSERT, DELETE, поскольку нам нужно управлять некластеризованным индексом в дополнение к данным в таблице. Тем более что у сервера есть закулисные задачи ... - person Ronen Ariely; 24.08.2018

Всего два цента.

Другой ответ хорош, но не касается идеи удаления единственного кластерного индекса. Для некоторых эта идея похожа на богохульство: D, но я видел это в действии.

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

Если ваша таблица является таблицей кластеризованных индексов (большинство из них в SQL Server), все другие некластеризованные индексы становятся «вторичными» индексами и, следовательно, не так эффективны. Если ваш запрос извлекает несколько строк (скажем, менее 10 тыс. Строк), вы не заметите «эффекта вторичного индекса». Когда запрос извлекает много строк, вы начинаете видеть эту проблему.

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

Совершенно другая стратегия, которую стоит рассмотреть хотя бы для целей сравнительного анализа, - это полное удаление кластерного индекса. Таким образом, все строки получат идентификатор строки, и внезапно все вторичные индексы станут настоящими первичными индексами. Если я не ошибаюсь, вам нужно будет (заново) создать свою таблицу как non-clustered; возможно, вы можете просто изменить его, но я не уверен.

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

person The Impaler    schedule 24.08.2018

Что касается комментария @SeanLange о том, что индексы являются искусством, а не наукой, лучшая панель foo, которую я видел, - это то, где все столбцы таблицы были в первичном ключе. Кроме того, если вы не будете осторожны и просто создадите индексы на основе каждого плана выполнения запроса, вы, вероятно, в конечном итоге сохраните в индексах больше данных, чем то, что находится в реальной таблице.

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

Обновление:

Я только что видел план выполнения запроса. Вы получаете результат при сканировании таблицы, что означает, что ни один из столбцов в предложении WHERE не содержится ни в первичном ключе, ни в индексе. Что касается оптимизатора, таблица работает в куче. Следовательно, любой добавляемый вами индекс, содержащий (т. Е. Покрывающий) столбцы, содержащиеся в предложении WHERE, скорее всего, будет использоваться. В результате запрос вернется намного быстрее.

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

person J Weezy    schedule 24.08.2018
comment
Ваш совет по поводу наличия индекса, охватывающего все запросы, заключается в том, как решить проблему, описанную в первом абзаце, когда тонны индексов занимают больше места, чем сама таблица. Вам нужно сбалансировать наличие нескольких общих, многоразовых, но полезных индексов с целевыми индексами специального назначения, чтобы охватить каждый отдельный запрос. - person Brandon; 25.08.2018
comment
@Brandon Как так? Я упомянул об использовании только столбцов первичного ключа. В моем примере с панелью foo все столбцы в таблице находились в первичном ключе. - person J Weezy; 25.08.2018
comment
@JWeezy: Как я описал, программное обеспечение должно сортировать по одному из множества столбцов в зависимости от пользовательских настроек. Так что я не понимаю, как это относится к этому. Кроме того, я понятия не имею, что означает SELECT на основе первичного ключа. - person Jonathan Wood; 28.08.2018
comment
@JonathanWood SELECT на основе первичного ключа - это еще один способ сказать, что первичный ключ находится в предложении WHERE - оптимизатор будет использовать индекс вместо сканирования таблицы (уродливо). Если вы разрешаете пользовательские настройки, то есть ли интерфейс приложения, который использует пользователь? Если да, почему бы не получить результаты и не позволить приложению отсортировать данные? Если нет, рассматривали ли вы возможность использования представления? Не уверен, что это поможет. Разрешение пользователю сортировки по различным столбцам может потребовать индекса для каждой комбинации сортировки (некрасиво). Вы можете рассмотреть возможность ограничения возможности сортировки пользователем. - person J Weezy; 28.08.2018
comment
@JWeezy: Если вы читаете мой вопрос, то знаете, что в таблице более 11 миллионов строк. Вы спрашиваете меня, почему я не извлекаю все строки из базы данных, а затем сортирую их в своем приложении? Извините, но это ужасный подход. Что касается выбора по первичному ключу, программное обеспечение имеет настройки фильтра, которые определяют, как выглядит предложение WHERE. Это не ограничивается одним столбцом. - person Jonathan Wood; 28.08.2018
comment
@JonathanWood Я не знаю вашу модель данных. Но похоже, что вы можете использовать запрос с предложением where, чтобы значительно уменьшить количество выбранных строк; доставить это в приложение; а затем позвольте приложению отсортировать сокращенный набор данных. Оказавшись в памяти, они могут упорядочить по любым наборам столбцов, которые они хотят, не загружая сервер дополнительными запросами. Вы должны работать с вашими пользователями, чтобы определить столбцы, которые имеют наибольшее значение при упорядочивании, что наиболее важно для построения правильного индекса (ов). В идеале вы сможете их рационализировать, а затем построить на основе этого точку зрения. - person J Weezy; 28.08.2018
comment
@JWeezy: Вы никогда не использовали программное обеспечение, которое позволяет вам сортировать по любому столбцу, который вы хотите? Кого из сотен пользователей следует спросить, от каких столбцов сортировки они готовы отказаться? И проблема с ограничением строк, возвращаемых из базы данных, иногда пользовательский фильтр приводит к миллионам строк. Итак, да, мы можем разбивать данные на страницы и получать по одной странице за раз, но мы не можем сделать это, пока не отсортируем данные! Как мы можем узнать, какие строки идут на первой странице, если это не так. уже не отсортированы? - person Jonathan Wood; 28.08.2018
comment
@JonathanWood Я не знаю, что это за приложение (веб-интерфейс?). Вы можете доставить данные на уровень приложения и отсортировать их с помощью кода, прежде чем отображать их пользователю. Кроме того, вы можете разделить таблицу, но все SELECTS должны использовать столбец раздела, это ускорит возврат результатов. Я предлагаю вам альтернативы и предостерегаю, позволяя пользователям возвращать миллионы записей только для их повторной сортировки. - person J Weezy; 28.08.2018
comment
@JWeezy: Ты полностью проигнорировал мои вопросы. Если я не знаю, какие строки идут на первую страницу, потому что данные еще не отсортированы, я не могу получить только это ограниченное количество строк. Вы просто повторили утверждение из своего предыдущего комментария. И не имеет значения, какой у меня интерфейс. - person Jonathan Wood; 28.08.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person J Weezy; 28.08.2018