7

编辑

@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 的任何时间,也没有减少行的相当大的额外大小。我的机器上的结果如下:

诠释:17,340 毫秒 GUID:6,746 毫秒

这意味着在这个测试中,随机插入 16 字节几乎比连续插入 4 字节快 3 倍

有人愿意对此发表评论吗?

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

4

3 回答 3

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

整数:18
秒引导:23 秒

量子点

于 2011-01-04T20:54:08.277 回答
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())
于 2011-01-04T20:33:49.643 回答
0

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

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

于 2011-01-04T20:51:07.427 回答