修改
@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> 。
有人想对此发表评论吗?
聚苯乙烯。我知道这不是一个问题。这是一个讨论的邀请,这与学习最佳编程有关。
答案 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不了解其他数据库。