Стратегия разделения узлов B-дерева в SQL Server для монотонно увеличивающегося значения

8

Рассмотрим индекс B-дерева для значения, которое всегда будет монотонно увеличиваться, например столбец типа IDENTITY. В обычной реализации B-дерева, когда узел заполнен, он будет разделен на 50% / 50%, и мы получим B-дерево, в котором (почти) все узлы будут заполнены только на 50%.

Я знаю, что Oracle обнаруживает, когда значение постоянно увеличивается, и в этих случаях Oracle выполняет разделение на 90% / 10%. Таким образом, (почти) все узлы будут заполнены на 90%, и в этих, довольно распространенных случаях достигается гораздо лучшее использование страниц.

Мне не удалось найти документацию для аналогичной функции в SQL Server. Однако я провел два эксперимента, в которых я вставил в индекс N случайных целых чисел и N последовательных целых чисел соответственно. В первом случае использовалось гораздо больше страниц, во втором.

Предоставляет ли SQL Server аналогичную функциональность? Если да, то можете ли вы указать мне некоторую документацию по этой функции?

ОБНОВЛЕНИЕ: Кажется, согласно экспериментам, представленным ниже, листовые узлы остаются неразделенными, а внутренние узлы делятся на 50% / 50%. Это делает B-деревья на увеличивающихся ключах более компактными, чем на случайных ключах. Тем не менее, подход Oracle на 90% / 10% еще лучше, и я все еще ищу некоторую официальную документацию, которая может проверить поведение, наблюдаемое в экспериментах.

SomeName
источник
Кажется, приемлемым ответом на этот вопрос, вероятно, будет некоторая документация, в которой перечислены все различные типы разбиения страниц, которые могут произойти и когда они могут произойти. Я в настоящее время не знаю о таком ресурсе, но, возможно, кто-то здесь ...
Мартин Смит

Ответы:

4

Если он добавляет строку в конец индекса, он просто выделит новую страницу для строки, а не разделит текущую конечную страницу. Экспериментальное подтверждение этому приведено ниже (используется %%physloc%%функция, которая требует SQL Server 2008). Смотрите также обсуждение здесь .

CREATE TABLE T
(
id int identity(1,1) PRIMARY KEY,
filler char(1000)
)
GO

INSERT INTO T
DEFAULT VALUES
GO 7

GO
SELECT sys.fn_PhysLocFormatter(%%physloc%%)
FROM T

GO

INSERT INTO T
DEFAULT VALUES

GO

SELECT sys.fn_PhysLocFormatter(%%physloc%%)
FROM T
GO

DROP TABLE T

Возвращает (Ваши результаты будут отличаться)

(1:173:0) /*File:Page:Slot*/
(1:173:1)
(1:173:2)
(1:173:3)
(1:173:4)
(1:173:5)
(1:173:6)
(1:110:0) /*Final insert is on a new page*/

Это, кажется, применимо только к конечным узлам. Это можно увидеть, запустив нижеприведенное и отрегулировав TOPзначение. Для меня 622/623была точка отсечения между требованием одной и двух страниц первого уровня (может отличаться, если у вас включена изоляция снимка?). Это делит страницу сбалансированным образом, что приводит к потере места на этом уровне.

USE tempdb;

CREATE TABLE T2
(
id int identity(1,1) PRIMARY KEY CLUSTERED,
filler char(8000)
)

INSERT INTO T2(filler)
SELECT TOP 622 'A'
FROM master..spt_values v1,  master..spt_values v2

DECLARE @index_info  TABLE
(PageFID  VARCHAR(10), 
  PagePID VARCHAR(10),   
  IAMFID   tinyint, 
  IAMPID  int, 
  ObjectID  int,
  IndexID  tinyint,
  PartitionNumber tinyint,
  PartitionID bigint,
  iam_chain_type  varchar(30),    
  PageType  tinyint, 
  IndexLevel  tinyint,
  NextPageFID  tinyint,
  NextPagePID  int,
  PrevPageFID  tinyint,
  PrevPagePID int, 
  Primary Key (PageFID, PagePID));

INSERT INTO @index_info 
    EXEC ('DBCC IND ( tempdb, T2, -1)'  ); 

DECLARE @DynSQL nvarchar(max) = 'DBCC TRACEON (3604);'
SELECT @DynSQL = @DynSQL + '
DBCC PAGE(tempdb, ' + PageFID + ', ' + PagePID + ', 3); '
FROM @index_info     
WHERE IndexLevel = 1

SET @DynSQL = @DynSQL + '
DBCC TRACEOFF(3604); '

EXEC(@DynSQL)


DROP TABLE T2
Мартин Смит
источник
Спасибо. Но обратите внимание, что я спрашиваю о поведении узлов индекса B-дерева, а не страниц таблицы. Хотя интересно читать. :-)
someName
1
@someName - страницы таблицы являются конечными узлами кластерного индекса, неявно созданного PRIMARY KEY.
Мартин Смит,
Ах я вижу. Эта стратегия вставки, безусловно, экономит место. Но я не вижу, как это вписывается в структуру B-дерева: с помощью стратегии «добавить-новую-страницу-вместо-разделения» мы получаем длинный связанный список, а не B-дерево. Как конкретные значения извлекаются с использованием только логарифмического числа поисков (входов / выходов) в этом связанном списке?
someName
Это только уровень конечного узла. Как только на уровне конечного узла будет более 1 страницы, появится еще один уровень выше. Вы можете использовать DBCC INDи, sys.dm_db_index_physical_statsчтобы увидеть информацию о них.
Мартин Смит,
Но всякий раз, когда один из неконечных узлов заполнен, я разделяюсь. И это разделение, я думаю, составляет 50% / 50%? Или 90% / 10%, как это делает Oracle?
someName