Поиск индекса против сканирования индекса

64

Глядя на план выполнения медленно выполняющегося запроса, я заметил, что некоторые узлы являются поиском по индексу, а некоторые - сканированием по индексу.

В чем разница между поиском по индексу и сканированием по индексу?

Который работает лучше?

Как SQL выбирает одно над другим?

Я понимаю, что это 3 вопроса, но я думаю, что ответ на первый объяснит остальные.

Greg
источник
6
У вас есть хорошая ссылка на use-the-index-luke .
Мариан
7
Не все сканы плохие - иногда это самый эффективный способ удовлетворить запрос. Также обратите внимание, что не все поиски являются поисками - часто это на самом деле сканирование диапазона, а поиск только указывает, как он попал в начало диапазона.
Аарон Бертран
@AaronBertrand, но если он доходит до начала диапазона и читает его, это в основном означает, что вам все равно нужны данные. Кроме того, он ищет конец диапазона.
Георгий Полевой

Ответы:

76

Короткая версия: искать гораздо лучше

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

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

Сканирование таблиц. При отсутствии индексов, относящихся к вашему запросу, планировщик вынужден использовать сканирование таблицы, что означает просмотр каждой строки. Это может привести к тому, что каждая страница, относящаяся к данным таблицы, будет прочитана с диска, что часто является наихудшим случаем. Обратите внимание, что для некоторых запросов он будет использовать сканирование таблицы даже при наличии полезного индекса - обычно это происходит из-за того, что данные в таблице настолько малы, что обходить индексы затруднительно (если это так, вы можете ожидать планируйте изменения по мере роста данных, предполагая, что показатель избирательности индекса хороший).

Сканирование по индексу с поиском строк: если не найден индекс, который можно напрямую использовать для поиска, но имеется индекс, содержащий правильные столбцы, можно использовать сканирование по индексу. Например, если у вас есть большая таблица с 20 столбцами с индексом column1, col2, col3 и вы выполняете команду SELECT col4 FROM exampletable WHERE col2=616, в этом случае сканирование индекса по запросу col2лучше, чем сканирование всей таблицы. Как только совпадающие строки найдены, страницы данных должны быть прочитаны для получения col4 для вывода (или дальнейшего объединения), что является стадией «поиска закладок», когда вы видите ее в планах запросов.

Сканирование по индексу без поиска строк: если в приведенном выше примере SELECT col1, col2, col3 FROM exampletable WHERE col2=616не потребовалось дополнительных усилий для чтения страниц данных: как только col2=616найдены соответствующие строки индекса , все запрошенные данные известны. Вот почему вы иногда видите столбцы, в которых никогда не производится поиск, но которые, вероятно, будут запрошены для вывода, добавлены в конец индексов - это может сохранить поиск строк. При добавлении столбцов в индекс по этой и только по этой причине добавьте их вместе с INCLUDEпредложением, чтобы сообщить движку, что ему не нужно оптимизировать макет индекса для запросов на основе этих столбцов (это может ускорить обновления, сделанные в этих столбцах). , Сканирование индекса также может быть результатом запросов без условий фильтрации: SELECT col2 FROM exampletableбудет сканироваться этот пример индекса вместо страниц таблицы.

Поиск по индексу (с поиском строк или без них) : при поиске учитывается не весь индекс. Для запроса SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567механизм запросов может найти первую строку, которая будет соответствовать, выполняя поиск по индексу на основе дерева, c1затем он может перемещаться по индексу по порядку, пока не достигнет конца диапазона (то же самое с запросом для , c1=1234как может быть много строк , соответствующих условию даже для =операции). Это означает, что нужно читать только соответствующие страницы индекса (плюс несколько страниц, необходимых для начального поиска) вместо каждой страницы в индексе (или таблице).

Кластерные индексы: при кластеризованном индексе данные таблицы хранятся в конечных узлах этого индекса, а не в отдельной структуре кучи. Это означает, что после поиска строк по этому индексу никогда не потребуется никаких дополнительных поисков строк, независимо от того, какие столбцы необходимы (если только у вас нет внестраничных данных, таких как TEXTстолбцы или VARCHAR(MAX)столбцы, содержащие длинные данные).

По этой причине у вас может быть только один кластеризованный индекс [1] , кластеризованный индекс - это ваша таблица, а не отдельная структура кучи, поэтому, если вы используете один [2], выберите место, куда вы его аккуратно поместили, чтобы получить максимальный выигрыш.

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

[1] На самом деле, вы можете эффективно иметь несколько кластеризованных индексов, определяя некластеризованные индексы, которые охватывают или включают каждый столбец в таблице, но это, вероятно, приводит к неэффективному использованию пространства, что влияет на производительность записи, поэтому, если вы решите это сделать, убедитесь, что тебе действительно нужно

[2] Когда я говорю « если вы используете кластерный индекс», не отметить , что , как правило , рекомендуется делать один на каждом столе. Существуют исключения, как и во всех эмпирических правилах. Таблицы, которые видят мало чего, кроме массовых вставок и неупорядоченных операций чтения (возможно, промежуточных таблиц для процессов ETL), являются наиболее распространенным контрпримером.

Дополнительный пункт: неполное сканирование:

Важно помнить, что в зависимости от остальной части запроса сканирование таблицы / индекса может фактически не сканировать всю таблицу - если логика позволяет, план запроса может вызвать преждевременный прерывание его выполнения. Простейший пример этого SELECT TOP(1) * FROM HugeTable- если вы посмотрите на план запроса для этого, вы увидите, что только одна строка была возвращена из сканирования, и если вы посмотрите статистику ввода-вывода ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable), вы увидите, что она читает только очень маленькое число страниц (возможно, только одна).

То же самое может произойти, если предикат предложения WHEREили JOIN ... ONможет быть запущен одновременно со сканированием, которое является источником его данных. Планировщик / исполнитель запросов иногда может быть очень умным в отношении толкания предикатов обратно к источникам данных, чтобы таким образом досрочно завершить сканирование (и иногда вы можете быть умными в перестановке запросов, чтобы помочь в этом!). В то время как данные передаются справа налево в соответствии со стрелками на стандартном отображении плана запроса, логика выполняется слева направо, и каждый шаг (справа налево) не обязательно выполняется до завершения следующего запуска. В приведенном выше простом примере, если вы посмотрите на каждый блок в плане запроса в качестве агента, SELECTагент запрашивает у TOPоператора строку, которая, в свою очередь, запрашиваетTABLE SCANагент для одного, затем SELECTагент запрашивает другого, но TOPагент знает, что в этом нет необходимости, даже не спрашивает читателя таблицы, SELECTагент получает ответ «больше не актуально» и знает, что вся работа выполнена. Многие операции блокируют этот вид оптимизации , конечно , так часто и в более сложных примерах таблицы / индекс сканирование действительно ли читать каждую строку, но будьте осторожны , чтобы не перейти к выводу , что любая проверка должна быть дорогостоящей операцией.

Дэвид Спиллетт
источник
6

Как правило, поиск - это хорошо, сканирование - плохо.

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

Сканирование - это когда запрос просматривает весь индекс, пытаясь найти то, что ему нужно.

Как выбирает SQL? В глубине души оптимизатора запросов решение принимается на основе вашего запроса, доступных индексов и статистической информации, связанной с этими индексами.

Здесь можно почитать несколько книг, которые могут быть интересны - обе из книжного магазина Red-Gate по адресу http://www.red-gate.com/community/books/.

  • Планы выполнения SQL Server от Гранта Фричи
  • Внутри оптимизатора запросов Бенджамин Неварез
  • Статистика SQL Server от Хольгера Шмелинга
Томас Руштон
источник
7
Для того же плана сканирование одной таблицы - это хорошо, миллион поисков - плохо. Так что ваше первое утверждение не совсем верно.
Мариан
Действительно, поиск по индексу и сканирование по индексу имеют свое использование, вы не можете сказать, что одно лучше другого, БЕЗ контекста базовых таблиц и запросов. В большинстве случаев, если у таблицы неточная статистика, план выполнения может оказаться неоптимальным, например, поиск по индексу по ошибке выбирается при сканировании индекса и наоборот.
17
5

Если вы хотите углубиться в тему, очень полезная книга (по крайней мере для меня) - «Планы выполнения SQL Server» Гранта Фричи, бесплатно доступные на RedGate здесь .

Если у вас есть запрос, такой как

SELECT *
FROM myTable

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

С другой стороны,

SELECT *
FROM myTable
WHERE myID = 1

безусловно, приведет к поиску индекса. SQL Server будет использовать структуру B-дерева индекса myID, и получение правильной строки будет намного быстрее.

KookieMonster
источник
Я не знаю, согласен ли я с «безусловно» - даже если индекс имеет myID в качестве ведущего столбца, поиск может не быть оптимальным ответом (зависит от многих факторов, например, является ли он уникальным - что может быть Значение true в таблице клиентов, но не для customerID в таблице заказов, сколько столбцов необходимо охватить, но их нет в индексе и т. д.).
Аарон Бертран
Я не думаю, что этот ответ действительно охватывает поставленные вопросы.
Zero3
5

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

Но в качестве примера, скажем, ваш код запрашивает столбец A и столбец B в заданных фильтрах, но вы также хотите вернуть значения столбца C и столбца E, вы можете создать индекс для столбцов A и B с помощью INCLUDE. Опция, содержащая столбцы C и E. Таким образом, поиск по одному индексу вернет все, что вам нужно, поскольку нет необходимости выполнять поиск для получения других значений (C и E) в той же строке.

Кан
источник