7

編集

@Remus がテスト パターンを修正しました。以下の彼の回答で修正版を見ることができます。

INT を DECIMAL(29,0) に置き換えることを提案したところ、結果は次のようになりました。

10 進数: 2133
GUID: 1836

わずかに大きな行であっても、ランダム挿入はまだ勝っています。

ランダムな挿入はシーケンシャルな挿入よりも遅いことを示す説明にもかかわらず、これらのベンチマークはそれらが明らかに速いことを示しています。私が得ている説明は、ベンチマークと一致していません。したがって、私の質問は引き続き b ツリー、順次挿入、および速度に焦点を当てています。

...

経験から、B ツリーにデータが (方向に関係なく) 順次追加されると、パフォーマンスが大幅に低下することがわかっています。ただし、データをランダムに追加すると、最高のパフォーマンスが得られます。

これは、RB ツリーなどを使用して簡単に実証できます。順次書き込みにより、最大数のツリー バランスが実行されます。

二分木を使用するデータベースはほとんどなく、n 次のバランスの取れた木を使用していることは知っています。シーケンシャルな入力に関しては、バイナリ ツリーと同様の運命をたどると論理的に想定しています。

これは私の好奇心を刺激しました。

その場合、シーケンシャル ID (IDENTITY(1,1) など) を書き込むと、ツリーの複数のリバランスが発生すると推測できます。「これらはランダムな書き込みを引き起こす」として、多くの投稿が GUID に反対しているのを見てきました。私は GUID を使用したことはありませんが、この「悪い」点が実際には良い点であることに気づきました。

だから私はそれをテストすることにしました。これが私のコードです:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T1](
    [ID] [int] NOT NULL
 CONSTRAINT [T1_1] PRIMARY KEY CLUSTERED ([ID] ASC) 
)
GO

CREATE TABLE [dbo].[T2](
    [ID] [uniqueidentifier] NOT NULL
 CONSTRAINT [T2_1] PRIMARY KEY CLUSTERED ([ID] ASC)
)

GO

declare @i int, @t1 datetime, @t2 datetime, @t3 datetime, @c char(300)

set @t1 = GETDATE()
set @i = 1

while @i < 2000 begin
    insert into T2 values (NEWID(), @c)
    set @i = @i + 1
end

set @t2 = GETDATE()
WAITFOR delay '0:0:10'
set @t3 = GETDATE()
set @i = 1

while @i < 2000 begin
    insert into T1 values (@i, @c)
    set @i = @i + 1
end

select DATEDIFF(ms, @t1, @t2) AS [Int], DATEDIFF(ms, @t3, getdate()) AS [GUID]

drop table T1
drop table T2

GUID の作成や、行のかなりの余分なサイズのために時間を差し引いていないことに注意してください。私のマシンでの結果は次のとおりです。

Int: 17,340 ミリ秒 GUID: 6,746 ミリ秒

これは、このテストでは、16 バイトのランダム挿入が 4 バイトの順次挿入よりもほぼ3 倍高速であることを意味します。

これについてコメントしたい人はいますか?

Ps。これは質問ではないことがわかりました。これは議論への招待であり、最適なプログラミングの学習に関連しています。

4

3 に答える 3

3

操作を反転すると、intが高速になります。ログとデータファイルの増加を考慮しましたか?それぞれを個別に実行する

declare @i int, @t1 datetime, @t2 datetime

set @t1 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T2 values (NEWID())
    set @i = @i + 1
END


set @t2 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T1 values (@i)
    set @i = @i + 1
end



select DATEDIFF(ms, @t1, @t2) AS [UID], DATEDIFF(ms, @t2, getdate()) AS [Int]

UUIDの問題は、UUIDをクラスタリングし、NEWSEQUENTIALID()を使用しない場合、ページ分割とテーブルの断片化が発生することです。

今、このように試してみてください、そしてあなたはそれがほとんど同じであることがわかります

declare @i int, @t1 datetime, @t2 datetime

set @t1 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T2 values (NEWID())
    set @i = @i + 1
END
select DATEDIFF(ms, @t1, getdate()) 

set @t1 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T1 values (@i)
    set @i = @i + 1
end



select DATEDIFF(ms, @t1, getdate())

そして逆転

declare @i int, @t1 datetime, @t2 datetime



set @t1 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T1 values (@i)
    set @i = @i + 1
end

set @t1 = GETDATE()
set @i = 1

while @i < 10000 begin
    insert into T2 values (NEWID())
    set @i = @i + 1
END
select DATEDIFF(ms, @t1, getdate())
于 2011-01-04T20:33:49.643 に答える
3

INSERT 速度を測定していません。ログ フラッシュのパフォーマンスを測定しています。各INSERTの後にコミットするため、これらのテストはすべて、コミットがログを強化するのを待っているだけです。これは、INSERT のパフォーマンスにはほとんど関係ありません。また、SET NOCOUNT がOFF...

では、適切なサイズのデータ​​、バッチ コミット、および事前に作成されたデータベースを使用して、不必要なサーバーとクライアントのおしゃべりなしでこれを試してみましょう。

:setvar dbname testdb
:setvar testsize 1000000
:setvar batchsize 1000

use master;
go

if db_id('$(dbname)') is not null
begin
    drop database [$(dbname)];
end
go

create database [$(dbname)] 
    on (name='test_data', filename='c:\temp\test_data.mdf', size=10gb)
    log on (name='test_log', filename='c:\temp\test_log.ldf', size=100mb);
go

use [$(dbname)];
go  

CREATE TABLE [dbo].[T1](
    [ID] [int] NOT NULL
 CONSTRAINT [T1_1] PRIMARY KEY CLUSTERED ([ID] ASC) 
)
GO

CREATE TABLE [dbo].[T2](
    [ID] [uniqueidentifier] NOT NULL
 CONSTRAINT [T2_1] PRIMARY KEY CLUSTERED ([ID] ASC)
)
GO

set nocount on;
go

declare @i int, @t1 datetime, @t2 datetime

set @t1 = GETDATE()
set @i = 1

begin transaction;
while @i < $(testsize) begin
    insert into T1 values (@i)
    set @i = @i + 1
    if @i % $(batchsize) = 0
    begin
        commit;
        begin transaction;
    end
end
commit

set @t2 = GETDATE()
set @i = 1
begin transaction
while @i < $(testsize) begin
    insert into T2 values (NEWID())
    set @i = @i + 1
    if @i % $(batchsize) = 0
    begin
        commit;
        begin transaction;
    end
end
commit

select DATEDIFF(ms, @t1, @t2) AS [Int], DATEDIFF(ms, @t2, getdate()) AS [UID]

drop table T1
drop table T2

INTS: 18 秒
GUID: 23秒

QED

于 2011-01-04T20:54:08.277 に答える
0

多くのインデックス エントリが 1 つのブロックに長く収まるため、実際のデータベースではインデックスの再調整は小さな問題であると予想しています。

さらに問題になる可能性があるのは、すべての新しいエントリを含む単一のブロックに対する競合です。Oracle には、新しいエントリをすべてのブロックに分散させるために、キーのバイトを逆順に格納する機能があります他のデータベースについては知りません。

于 2011-01-04T20:51:07.427 に答える