В хранилище, ориентированном на строки в SQL Server, как кластерные, так и некластерные индексы организованы в виде B-деревьев.
( Источник изображения )
Основное различие между кластеризованными и некластеризованными индексами состоит в том, что конечным уровнем кластеризованного индекса является таблица. Это имеет два значения.
- Строки на листовых страницах кластерного индекса всегда содержат что-то для каждого (не разреженного) столбца таблицы (либо значение, либо указатель на фактическое значение).
- Кластерный индекс является основной копией таблицы.
Некластеризованные индексы могут также выполнить пункт 1, используя INCLUDE
предложение (начиная с SQL Server 2005), чтобы явно включить все неключевые столбцы, но они являются вторичными представлениями, и всегда есть другая копия данных (сама таблица).
CREATE TABLE T
(
A INT,
B INT,
C INT,
D INT
)
CREATE UNIQUE CLUSTERED INDEX ci ON T(A,B)
CREATE UNIQUE NONCLUSTERED INDEX nci ON T(A,B) INCLUDE (C,D)
Два индекса выше будут почти идентичны. С индексными страницами верхнего уровня, содержащими значения для ключевых столбцов, A,B
и страницами конечного уровня, содержащимиA,B,C,D
В таблице может быть только один кластеризованный индекс, поскольку сами строки данных могут быть отсортированы только в одном порядке.
Приведенная выше цитата из книг по SQL Server в Интернете вызывает много путаницы
На мой взгляд, это было бы гораздо лучше сформулировать как.
В таблице может быть только один кластеризованный индекс, потому что строки уровня листьев кластеризованного индекса являются строками таблицы.
Онлайновая цитата из книг не является неправильной, но вы должны понимать, что «сортировка» как некластеризованных, так и кластеризованных индексов является логической, а не физической. Если вы читаете страницы на уровне листа, следуя связанному списку, и читаете строки на странице в порядке расположения слотов, то вы будете читать строки индекса в отсортированном порядке, но физически страницы могут быть не отсортированы. Обычно считается, что при кластеризованном индексе строки всегда физически хранятся на диске в том же порядке, что и ключ индекса, неверно.
Это было бы абсурдной реализацией. Например, если строка вставлена в середину таблицы 4 ГБ, SQL Server не нужно копировать 2 ГБ данных в файл, чтобы освободить место для вновь вставленной строки.
Вместо этого происходит разделение страницы. Каждая страница на уровне листьев как кластеризованных, так и некластеризованных индексов имеет адрес ( File:Page
) следующей и предыдущей страницы в порядке логического ключа. Эти страницы не обязательно должны быть смежными или в ключевом порядке.
например, цепочка связанных страниц может быть 1:2000 <-> 1:157 <-> 1:7053
Когда происходит разделение страницы, новая страница выделяется из любой точки файловой группы (из смешанного экстента, для небольших таблиц, или из непустого единообразного экстента, принадлежащего этому объекту, или из вновь выделенного единообразного экстента). Это может даже не быть в том же файле, если файловая группа содержит больше чем один.
Степень, в которой логический порядок и смежность отличаются от идеализированной физической версии, является степенью логической фрагментации.
Во вновь созданной базе данных с одним файлом я запустил следующее.
CREATE TABLE T
(
X TINYINT NOT NULL,
Y CHAR(3000) NULL
);
CREATE CLUSTERED INDEX ix
ON T(X);
GO
--Insert 100 rows with values 1 - 100 in random order
DECLARE @C1 AS CURSOR,
@X AS INT
SET @C1 = CURSOR FAST_FORWARD
FOR SELECT number
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 100
ORDER BY CRYPT_GEN_RANDOM(4)
OPEN @C1;
FETCH NEXT FROM @C1 INTO @X;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO T (X)
VALUES (@X);
FETCH NEXT FROM @C1 INTO @X;
END
Затем проверил макет страницы с помощью
SELECT page_id,
X,
geometry::Point(page_id, X, 0).STBuffer(1)
FROM T
CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
ORDER BY page_id
Результаты были повсюду. Первая строка в ключевом порядке (со значением 1 - выделено стрелкой ниже) была почти на последней физической странице.
Фрагментация может быть уменьшена или удалена путем перестройки или реорганизации индекса, чтобы увеличить корреляцию между логическим порядком и физическим порядком.
После запуска
ALTER INDEX ix ON T REBUILD;
Я получил следующее
Если таблица не имеет кластеризованного индекса, она называется кучей.
Некластеризованные индексы могут быть построены либо на куче, либо на кластерном индексе. Они всегда содержат локатор строк обратно к базовой таблице. В случае кучи это физический идентификатор строки (rid) и состоит из трех компонентов (File: Page: Slot). В случае кластеризованного индекса указатель строки является логическим (ключ кластеризованного индекса).
В последнем случае, если некластеризованный индекс уже содержит столбцы ключа CI либо в виде столбцов ключа NCI, либо INCLUDE
столбца -d, то ничего не добавляется. В противном случае отсутствующие ключевые столбцы CI автоматически добавляются в NCI.
SQL Server всегда гарантирует, что ключевые столбцы уникальны для обоих типов индекса. Механизм, в котором это применяется для индексов, не объявленных как уникальные, отличается между двумя типами индексов.
Кластерные индексы uniquifier
добавляются для любых строк с ключевыми значениями, которые дублируют существующую строку. Это просто восходящее целое число.
Для некластеризованных индексов, не объявленных как уникальные, SQL Server автоматически добавляет локатор строк в ключ некластеризованного индекса. Это относится ко всем строкам, а не только к тем, которые на самом деле являются дубликатами.
Кластеризованная и некластерная номенклатура также используется для индексов хранилища столбцов. В статье Усовершенствования столбцов Stores SQL Server состояний
Хотя данные хранилища столбцов на самом деле не «кластеризованы» ни по одному ключу, мы решили сохранить традиционное соглашение SQL Server, относящееся к первичному индексу как к кластерному индексу.