Глядя на план выполнения медленно выполняющегося запроса, я заметил, что некоторые узлы являются поиском по индексу, а некоторые - сканированием по индексу.
В чем разница между поиском по индексу и сканированием по индексу?
Который работает лучше?
Как SQL выбирает одно над другим?
Я понимаю, что это 3 вопроса, но я думаю, что ответ на первый объяснит остальные.
Ответы:
Короткая версия: искать гораздо лучше
Менее короткая версия: поиск, как правило, намного лучше, но большое количество запросов (вызванных, например, неправильным дизайном запросов с неприятными коррелированными подзапросами или выполнением большого количества запросов в операции курсора или в другом цикле) может быть хуже сканировать, особенно если ваш запрос может в конечном итоге вернуть данные из большинства строк в затронутой таблице.
Это помогает охватить всю семью операциями по поиску данных, чтобы полностью понять последствия для производительности.
Сканирование таблиц. При отсутствии индексов, относящихся к вашему запросу, планировщик вынужден использовать сканирование таблицы, что означает просмотр каждой строки. Это может привести к тому, что каждая страница, относящаяся к данным таблицы, будет прочитана с диска, что часто является наихудшим случаем. Обратите внимание, что для некоторых запросов он будет использовать сканирование таблицы даже при наличии полезного индекса - обычно это происходит из-за того, что данные в таблице настолько малы, что обходить индексы затруднительно (если это так, вы можете ожидать планируйте изменения по мере роста данных, предполагая, что показатель избирательности индекса хороший).
Сканирование по индексу с поиском строк: если не найден индекс, который можно напрямую использовать для поиска, но имеется индекс, содержащий правильные столбцы, можно использовать сканирование по индексу. Например, если у вас есть большая таблица с 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
агент получает ответ «больше не актуально» и знает, что вся работа выполнена. Многие операции блокируют этот вид оптимизации , конечно , так часто и в более сложных примерах таблицы / индекс сканирование действительно ли читать каждую строку, но будьте осторожны , чтобы не перейти к выводу , что любая проверка должна быть дорогостоящей операцией.источник
Как правило, поиск - это хорошо, сканирование - плохо.
Поиск - это то, где запрос может эффективно использовать индекс и использовать его для поиска нужных ему строк.
Сканирование - это когда запрос просматривает весь индекс, пытаясь найти то, что ему нужно.
Как выбирает SQL? В глубине души оптимизатора запросов решение принимается на основе вашего запроса, доступных индексов и статистической информации, связанной с этими индексами.
Здесь можно почитать несколько книг, которые могут быть интересны - обе из книжного магазина Red-Gate по адресу http://www.red-gate.com/community/books/.
источник
Если вы хотите углубиться в тему, очень полезная книга (по крайней мере для меня) - «Планы выполнения SQL Server» Гранта Фричи, бесплатно доступные на RedGate здесь .
Если у вас есть запрос, такой как
SQL Server, скорее всего, будет использовать сканирование индекса, так как ему нужно пройти по всем строкам, чтобы отобразить требуемые результаты.
С другой стороны,
безусловно, приведет к поиску индекса. SQL Server будет использовать структуру B-дерева индекса myID, и получение правильной строки будет намного быстрее.
источник
Другие достаточно хорошо определили различия между поиском и сканированием. В этом случае сам запрос и планировщик выполнения должны предоставить вам необходимую информацию, чтобы увидеть, какие значения используются в качестве предикатов (фильтров) для запроса в каждой части. Обычно рекомендуется всегда добавлять некластеризованные индексы к внешним ключам, и в зависимости от случаев использования в программном коде может потребоваться создание дополнительных многостолбечных индексов или включенных индексов столбцов. С терминологией, представленной здесь, поиск в Google даст достойные результаты на примерах по каждому.
Но в качестве примера, скажем, ваш код запрашивает столбец A и столбец B в заданных фильтрах, но вы также хотите вернуть значения столбца C и столбца E, вы можете создать индекс для столбцов A и B с помощью INCLUDE. Опция, содержащая столбцы C и E. Таким образом, поиск по одному индексу вернет все, что вам нужно, поскольку нет необходимости выполнять поиск для получения других значений (C и E) в той же строке.
источник