B树,数据库,顺序与随机插入和速度。随机赢了

时间:2011-01-04 20:25:30

标签: sql sql-server benchmarking clustered-index

修改

@Remus纠正了我的测试模式。您可以在下面的答案中看到更正后的版本。

我接受了用DECIMAL(29,0)替换INT的建议,结果是:

  

十进制:2133
  GUID:1836

随机插入仍然会赢,即使行数稍大一些。

尽管解释表明随机插入比连续插入慢,但这些基准测试表明它们显然更快。我得到的解释不符合基准。因此,我的问题仍然集中在b树,顺序插入和速度上。

...

我从经验中知道,当数据按顺序添加到它们时(无论方向如何),b树的性能都很糟糕。但是,当随机添加数据时,可以获得最佳性能。

这很容易用RB-Tree来演示。顺序写入会导致执行最大数量的树余额。

我知道很少有数据库使用二叉树,而是使用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 ms GUID:6,746 ms

这意味着在此测试中, 16字节的随机插入 4字节的顺序插入快<3>

有人想对此发表评论吗?

聚苯乙烯。我知道这不是一个问题。这是一个讨论的邀请,这与学习最佳编程有关。

3 个答案:

答案 0 :(得分: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的问题是当它们聚集在一起而不使用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())

答案 1 :(得分: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:18s
GUIDS:23s

QED

答案 2 :(得分:0)

我希望在真正的数据库中重新平衡索引是一个小问题,因为很多索引条目都适合单个块并且只需要很长时间。

可能会成为更多问题的可能是争用包含所有新条目的单个块。 Oracle具有以相反顺序存储密钥字节的功能,以便在所有块上传播新条目:http://oracletoday.blogspot.com/2006/09/there-is-option-to-create-index.html不了解其他数据库。