Estratégia de divisão de nó de árvore B no SQL Server para aumentar monotonicamente o valor

8

Considere um índice da árvore B com um valor que sempre aumentará monotonicamente, por exemplo, uma coluna do tipo IDENTITY. Com uma implementação convencional da árvore B, sempre que um nó estiver cheio, ele será dividido em 50% / 50% e terminaremos com uma árvore B na qual (quase) todos os nós estarão apenas 50% cheios.

Eu sei que a Oracle descobre quando um valor está sempre aumentando e, nesses casos, a Oracle realiza uma divisão de 90% / 10%. Dessa forma, (quase) todos os nós estarão 90% cheios e uma utilização de página muito melhor será obtida para esses casos bastante comuns.

Não consegui encontrar documentação para um recurso semelhante no SQL Server. No entanto, realizei duas experiências nas quais inseri N números aleatórios e N números consecutivos em um índice, respectivamente. O primeiro caso usou muito mais páginas do segundo.

O SQL Server fornece uma funcionalidade semelhante? Em caso afirmativo: você pode me indicar alguma documentação sobre esse recurso?

ATUALIZAÇÃO: Parece, pelas experiências fornecidas abaixo, que os nós das folhas são mantidos sem divisão e os nós internos são divididos em 50% / 50%. Isso torna as árvores B ao aumentar as chaves mais compactas do que nas chaves aleatórias. No entanto, a abordagem de 90% / 10% da Oracle é ainda melhor e ainda procuro alguma documentação oficial que possa verificar o comportamento observado nas experiências.

someName
fonte
Parece que uma resposta aceitável para essa pergunta provavelmente seria uma documentação que lista todos os tipos de divisão de página que podem ocorrer e quando podem ocorrer. Eu não estou atualmente ciente de tal recurso um, mas talvez alguém aqui é ...
Martin Smith

Respostas:

4

Se estiver adicionando uma linha no final do índice, apenas alocará uma nova página para a linha, em vez de dividir a página final atual. A evidência experimental para isso está abaixo (usa a %%physloc%%função que requer o SQL Server 2008). Veja também a discussão aqui .

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

Retornos (seus resultados variam)

(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*/

Porém, isso parece se aplicar apenas aos nós das folhas. Isso pode ser visto executando o abaixo e ajustando o TOPvalor. Para mim, 622/623foi o ponto de corte entre exigir uma e duas páginas de primeiro nível (pode variar se o isolamento de instantâneo estiver ativado?). Ele divide a página de maneira equilibrada, levando ao desperdício de espaço nesse nível.

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
Martin Smith
fonte
Obrigado. Mas observe que estou solicitando o comportamento dos nós de índice da árvore B - não das páginas da tabela. Leitura interessante embora. :-)
someName
11
@someName - As páginas da tabela são os nós das folhas do índice clusterizado implicitamente criado pelo PRIMARY KEY.
Martin Smith
Ah entendo. Essa estratégia de inserção é certamente eficiente em termos de espaço. Mas não vejo como isso se encaixa na estrutura da árvore B: com a estratégia "adicionar à nova página em vez de dividir", terminamos com uma lista longa e vinculada, e não com uma árvore B. Como os valores específicos são recuperados usando apenas um número logarítmico de pesquisas (E / Ss) nesta lista vinculada?
usar o seguinte código
Este é apenas o nível do nó folha. Assim que o nível do nó folha tiver mais de 1 página, haverá outro nível acima. Você pode usar DBCC INDe sys.dm_db_index_physical_statsver informações sobre eles.
Martin Smith
Mas sempre que um dos nós não-folha estiver cheio, eu serei dividido. E essa divisão, eu acho, é de 50% / 50%? Ou 90% / 10% como a Oracle faz?
usar o seguinte código