SQL Server 中的 B 树节点拆分策略用于单调递增值

som*_*ame 8 index sql-server fragmentation page-splits

考虑一个总是单调增加的值的 B 树索引,例如 IDENTITY 类型的列。使用传统的 B 树实现,每当节点已满时,它将被拆分 50%/50%,我们最终得到一个 B 树,其中(几乎)所有节点都只有 50% 已满。

我知道 Oracle 会发现值何时不断增加,在这些情况下,Oracle 会执行 90%/10% 的拆分。这样,(几乎)所有节点都将充满 90%,并且对于这些非常常见的情况,可以获得更好的页面利用率。

我无法在 SQL Server 中找到类似功能的文档。但是,我进行了两次实验,分别在索引中插入了 N 个随机整数和 N 个连续整数。前一种情况使用了更多的页面,后者。

SQL Server 是否提供类似的功能?如果是这样:你能指点我一些关于这个功能的文档吗?

更新: 通过下面提供的实验,似乎叶节点保持未分裂,内部节点分裂 50%/50%。这使得增加键上的 B 树比随机键上的更紧凑。然而,Oracle 的 90%/10% 方法甚至更好,我仍然在寻找一些官方文档来验证实验中看到的行为。

Mar*_*ith 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
Run Code Online (Sandbox Code Playgroud)

返回(您的结果会有所不同)

(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*/
Run Code Online (Sandbox Code Playgroud)

但这似乎只适用于叶节点。这可以通过运行下面的命令并调整值来看到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
Run Code Online (Sandbox Code Playgroud)