Skip to content

Поиск в кластеризованном индексе (Clustered Index Seek)

Пересказ статьи Hugo Kornelis. Clustered Index Seek


Введение


Оператор Clustered Index Seek использует структуру кластеризованного индекса для эффективного поиска как отдельных строк (singleton seek), так и конкретных подмножеств строк (range seek). Поскольку кластеризованный индекс всегда содержит все столбцы таблицы, Clustered Index Seek является одним из наиболее эффективных приемов, который применяет SQL Server для поиска отдельных строк и небольших диапазонов при условии наличия фильтра, который может быть эффективно использован.
Фактически поведение оператора Clustered Index Seek в точности то же, что и поведение оператора Index Seek, за незначительными отличиями, отмечаемыми ниже. Эти два оператора имеют разные названия не только на графическом плане выполнения, но и в соответствующем XML, И я подозреваю, что на самом деле они оба используют одну и ту же внутреннюю логику, а не копию её.

Одно из отличий между Clustered Index Seek и Index Seek состоит в диапазоне типов индексов, которые могут использоваться, что отмечается в свойстве Storage (хранилище). В то время как Index Seek поддерживает и индексы построчного хранения (RowStore) и индексы, оптимизированные для памяти (MemoryOptimized), оператор Clustered Index Seek может работать только с индексами построчного хранения. Причина этого различия проста - ни текущая версия SQL Server (2019), ни более старые версии, не имеют поддержки оптимизированных для памяти кластеризованных индексов, а значит невозможно выполнять поиск в индексе, который не может быть создан.

Визуальное представление планов выполнения


В зависимости от используемого инструментария, оператор Clustered Index Seek может на графическом плане выполнения выглядеть так:


SQL Server Management Studio (версия 17.4 и выше)


SQL Server Management Studio (до версии 17.3)


Azure Data Studio


Plan Explorer

Алгоритм


Для подробного описания алгоритма поиска Clustered Index Seek обратитесь к описанию алгоритма оператора Index Seek. Как утверждалось выше, эти два оператора настолько похожи, что я подозреваю просто два названия для одних и тех же блоков кода в движке SQL Server.

Специфичное поведение


Хотя Clustered Index Seek по сути то же самое, что и Index Seek, могут иметь место тонкие различия в поведении в зависимости от типа индекса. В последующих параграфах все они перечислены.

Поколоночный


Для поколоночных индексов кластеризованные и некластеризованные индексы строятся в соответствии с одной и той же базовой структурой, известной как B-Tree (сбалансированное дерево). Имеется несколько тонких различий, связанных с точным размещением битов на странице, но обсуждение этого выходит за рамки настоящей статьи.

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

Из-за этого различия кластеризованный индекс может всегда предоставить все данные, которые требуются оптимизатору на последующих этапах плана выполнения. Index Seek может предоставить все необходимые данные только в том случае, если множество столбцов, необходимых запросу, является подмножеством столбцов, находящихся на листовых страницах сканируемого некластеризованного индекса. Поэтому потом может потребоваться добавление оператора Nested Loops (вложенные циклы) либо в оператор Key Lookup (поиск ключа), либо в RID Lookup (поиск указателя) для получения всех данных. Для Clustered Index Seek никакой дополнительный поиск не требуется.

Оптимизированный для памяти


На время написания этой статьи никакая из существующих версий SQL Server (2019 и ниже) не поддерживала кластеризованные индексы оптимизированные для памяти. Теоретически возможно, чтобы оператор Clustered Index Seek уже мог иметь доступ к кластеризованным оптимизированным для памяти индексам, но, поскольку такие индексы не могут быть созданы, невозможно проверить это без доступа к исходным кодам.

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

Единичный поиск или поиск в диапазоне


Поскольку SQL Server по умолчанию создает кластеризованный индекс на первичном ключе, большинство кластеризованный индексов объявляются уникальными. В результате бытует общее заблуждение, что кластеризованные индексы всегда создаются как UNIQUE индексы. Это неверно, при явном создании кластеризованного индекса он может быть объявлен неуникальным. В этом случае скрытый дополнительный столбец, который называется uniqueifier (уникальным идентификатором), добавляется автоматически. Этот уникальный идентификатор не может использоваться в запросах, но его могут использовать процессы SQL Server.

Для Clustered Index Seek спецификация поисковых ключей (Seek keys) считается одиночным поиском, если выполняются оба следующих условия:

  1. Поисковые ключи не должны иметь спецификации диапазона (Range), что подразумевает наличие префиксной спецификации (Prefix);

  2. Спецификация Prefix должна предоставлять значения для всех столбцов в спецификации индекса, так например, если индекс определен на трех столбцах, все три столбца должны быть включены в спецификацию Prefix поисковых ключей. Если кластеризованный индекс не объявлен как UNIQUE, то спецификация Prefix также должна включать столбец uniqueifier.


Свойства оператора


Нижеприведенные свойства специфичны для оператора Clustered Index Seek или же имеют специфичный смысл для него. Относительно всех других свойств обратитесь к "Общие свойства". Свойства, которые находятся на странице общих свойств, но также включены ниже по причине специфики их смысла для оператора поиска в кластеризованном индексе, отмечаются значком *.

(Заметим, что большинство из этих свойств аналогичны для оператора Index Seek; они повторяются для удобства).














Название свойстваОписание
Defined Values
(определенные значения)
*
Для Clustered Index Seek это свойство перечисляет столбцы, прочитанные из индекса и возвращаемые в вызывающий оператор. Т.е. это то же самое, что и свойство Output List.
Estimated Number of Rows to be Read (оценка числа считываемых строк)Это оценка числа строк, которое будет прочитано оператором при навигации по индексу. Чем выше разница между этим числом и Table Cardinality, тем более эффективным оценивается использование этого оператора (по сравнению с Clustered Index Scan). Разница между этим свойством и свойством Estimated Number of Rows представляет число строк, которые по оценке должны быть прочитаны, но не будут возвращены в силу свойства Predicate.
Forced Index (навязанный индекс)Это свойство устанавливается в true, если использование этого индекса было навязано хинтом запроса.
ForceScan (навязанное сканирование)Это свойство устанавливается в true, если запрос использовал хинт FORCESCAN для принудительного использования оператора сканирования, даже если оптимизатор предпочел бы использовать оператор поиска. Следовательно, в операторе Clustered Index Seek он всегда ложен.
ForceSeek (навязанный поиск)Это свойство устанавливается в true, если запрос использовал хинт FORCESEEK для принудительного использования оператора поиска, даже если оптимизатор предпочел бы использовать оператор сканирования.
IndexKind (вид индекса)Представляет тип сканируемого кластеризованного индекса; оно всегда равно Clustered для Clustered Index Seek. Замечу, что это свойство не отображается в списке SSMS, хотя тип индекса показывается в скобках под оператором. К свойству можно получить доступ в плане выполнения XML.
NoExpandHintЭто свойство устанавливается в true, в запросе был использован хинт NOEXPAND для принуждения оптимизатора к использованию индексов на индексированном представлении.
Number of Rows Read (число чтений строк)Это число строк, которые были прочитаны оператором при навигации по индексу. В параллельном плане выполнения, это свойство показывает разбивку строк по каждому отдельному процессу. Разница между этим свойством и свойством Actual Number of Rows представляет число строк, которые были прочитаны, но не возвращены в силу свойства Predicate. Доступно только в плане выполнения плюс статистика времени выполнения. Когда число чтений строк равно нулю, это свойство опускается.
ObjectЭто свойство перечисляет индекс, по которому перемещается оператор Index Seek, используя именование из 4 частей (база, схема, таблица, индекс), и может иметь последующий алиас.
OrderedЭто свойство всегда равно True для Index Seek.
Predicate (предикат)Если присутствует, это свойство определяет логическое выражение, которое должно применяться ко всем строкам, прочитанных оператором Clustered Index Seek. Возвращаются только те строки, для которых предикат оценивается как True. При возможности оператор Clustered Index Seek будет проталкивать этот предикат в движок хранилища, чтобы избежать лишних переходов между оператором и движком. Заметим, что Predicate для Clustered Index Seek не уменьшает числа строк, на которые воздействует оператор. Разница между Actual Number of Rows и свойством Number of Rows Read (или их предварительными аналогами) показывает как много прочитанных строк не было возвращено. Это можно использовать для оценки полезности индекса относительно поддержки поисковых предикатов.
Scan Direction (направление сканирования)Для поиска в диапазоне это свойство определяет, в каком порядке будут возвращаться данные - в обычном порядке индекса (“FORWARD”) или в обратном порядке (“BACKWARD”). Опция BACKWARD недоступна для индексов, оптимизированных для памяти.
Seek Predicates (поисковые предикаты)Набор одного или более ключей поиска, которые используются для навигации по индексу и нахождения необходимых для чтения строк.
Storage (хранилище)Это свойство определяет тип индекса, по которому выполняется навигация. (отметим, что это также может быть определено с помощью DMV на основе свойства Object). В настоящее время для Clustered Index Seek единственным вариантом является RowStore.
Table Cardinality (кардинальное число таблицы)Это свойство показывает число строк в проиндексированной таблице на момент компиляции плана.


Неявные свойства


В таблице ниже перечислено поведение неявных свойств для оператора Clustered Index Seek.
(Заметим, что большинство из этих свойств аналогичны свойствам оператора Index Seek и повторяются здесь для удобства).






Название свойстваОписание
Batch Mode enabled (пакетный режим разрешен)Оператор Clustered Index Seek поддерживает только построчный режим.
Blocking (блокировка)Оператор Clustered Index Seek является неблокирующим.
Memory requirement (потребность в памяти)Оператор Clustered Index Seek не предъявляет каких-либо особых требований к памяти.
Order-preserving (сохранение порядка)Оператор Clustered Index Seek навязывает порядок, что определяется свойством Scan Direction. Если свойство Seek Predicates задает более одной спецификации Seek Keys, оптимизатор всегда гарантирует их соответствие правильному порядку.
Поддержка параллелизмаКогда оператор Clustered Index Seek используется для сканирования диапазонов в параллельной части плана выполнения, он использует “Parallel Page Supplier”. Смотрите детали для Index Scan.
Segment aware (поддержка сегментов)Оператор Clustered Index Seek не поддерживает сегменты.



Обратные ссылки

Нет обратных ссылок

Комментарии

Показывать комментарии Как список | Древовидной структурой

Нет комментариев.

Автор не разрешил комментировать эту запись

Добавить комментарий

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

Form options

Добавленные комментарии должны будут пройти модерацию прежде, чем будут показаны.