単調に増加する値のためのSQL ServerのBツリーノード分割戦略


8

IDENTITY型の列など、常に単調に増加する値のBツリーインデックスについて考えます。従来のBツリーの実装では、ノードがいっぱいになると常に50%/ 50%に分割され、(ほとんど)すべてのノードが50%だけいっぱいになるBツリーになります。

私は、Oracleが値が増加し続けるときを発見し、そのような場合にOracleが90%/ 10%の分割を実行することを知っています。そうすれば、(ほぼ)すべてのノードが90%満たされ、これらの非常に一般的なケースではるかに優れたページ使用率が得られます。

SQL Serverの同様の機能に関するドキュメントを見つけることができませんでした。ただし、N個のランダムな整数とN個の連続した整数をそれぞれインデックスに挿入する2つの実験を実行しました。前者のケースは後者をはるかに多くのページを使用しました。

SQL Serverは同様の機能を提供しますか?もしそうなら、あなたは私にこの機能に関するいくつかのドキュメントを教えてもらえますか?

更新: 以下の実験では、リーフノードは分割されず、内部ノードは50%/ 50%に分割されているようです。これにより、増加するキーのBツリーは、ランダムキーよりもコンパクトになります。ただし、Oracleによる90%/ 10%アプローチはさらに優れており、実験で確認された動作を検証できる公式ドキュメントを探しています。


この質問に対する許容できる回答は、発生する可能性のあるさまざまな種類のページ分割とそれらが発生する可能性のあるタイミングをすべてリストしたドキュメントである可能性があります。私は現在そのようなリソースに気づいていませんが、多分ここに誰かがいます...
Martin Smith

回答:


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は、1番目と2番目の最初のレベルのページを要求することの間のカットオフポイントでした(スナップショット分離を有効にした場合、異なる場合がありますか?)。ページをバランスの取れた方法で分割し、このレベルでスペースを無駄にします。

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ツリーではなく、長いリンクリストになります。このリンクされたリストでルックアップ(I / O)の対数のみを使用して特定の値を取得するにはどうすればよいですか?
someName

これはリーフノードレベルのみです。リーフノードレベルに複数のページがあるとすぐに、その上に別のレベルがあります。とを使用DBCC INDsys.dm_db_index_physical_statsて、これらに関する情報を表示できます。
マーティン・スミス

しかし、非リーフノードの1つがいっぱいになると、分割されます。そして、その分割は50%/ 50%でしょうか?それともOracleの90%/ 10%ですか?
someName
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.