sql-server – 为什么在我的测试用例中,顺序GUID键的执行速度比顺序INT键快?

在询问 this问题比较顺序和非顺序GUID之后,我尝试比较INSERT性能:1)一个表与GUID主键顺序初始化newsequentialid(),和2)一个表,INT主键按顺序初始化(1,1).我希望后者最快,因为整数宽度较小,生成顺序整数似乎比顺序GUID更简单.但令我惊讶的是,带有整数键的表上的INSERT明显慢于顺序GUID表.

显示了测试运行的平均时间使用(ms):

NEWSEQUENTIALID()  1977
IDENTITY()         2223

有谁能解释一下?

使用以下实验:

SET NOCOUNT ON

CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,SomeDate DATETIME,batchNumber BIGINT,FILLER CHAR(100))

CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestInt (SomeDate,@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

DBCC showcontig ('TestGuid2')  WITH tableresults
DBCC showcontig ('TestInt')  WITH tableresults

SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber

SELECT batchNumber,MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber

DROP TABLE TestGuid2
DROP TABLE TestInt

更新:
修改脚本以执行基于TEMP表的插入,如下面的Phil Sandler,Mitch Wheat和Martin的示例,我也发现IDENTITY应该更快.但这不是传统的插入行的方式,我仍然不明白为什么实验最初会出错:
即使我从原始示例中省略了GETDATE(),IDENTITY()仍然会慢一些.因此,似乎使IDENTITY()的性能优于NEWSEQUENTIALID()的唯一方法是准备要插入临时表的行,并使用此临时表执行多次插入作为批量插入.总而言之,我认为我们没有找到对这种现象的解释,对于大多数实际用法而言,IDENTITY()似乎仍然较慢.有谁能解释一下?

解决方法

修改了@Phil Sandler的代码,以消除调用GETDATE()的影响(可能涉及硬件效果/中断??),并使行长度相同.

[自sql Server 2000以来,有几篇文章涉及时序问题和高分辨率计时器,所以我想尽量减少这种影响.]

在简单的恢复模型中,数据和日志文件的大小都超过了所需的大小,这里是时间(以秒为单位):(根据下面的确切代码更新了新结果)

Identity(s)  Guid(s)
       ---------    -----
       2.876        4.060    
       2.570        4.116    
       2.513        3.786   
       2.517        4.173    
       2.410        3.610    
       2.566        3.726
       2.376        3.740
       2.333        3.833
       2.416        3.700
       2.413        3.603
       2.910        4.126
       2.403        3.973
       2.423        3.653
    -----------------------
Avg    2.650        3.857
StdDev 0.227        0.204

使用的代码

SET NOCOUNT ON

CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,FILLER CHAR(88))

CREATE TABLE TestInt (Id Int NOT NULL identity(1,FILLER CHAR(100))

DECLARE @Numrows INT = 1000000

CREATE TABLE #temp (Id int NOT NULL Identity(1,rowNum int,adate datetime)

DECLARE @LocalCounter INT = 0

--put rows into temp table
WHILE (@LocalCounter < @NumRows)
BEGIN
    INSERT INTO #temp(rowNum,adate) VALUES (@LocalCounter,GETDATE())
    SET @LocalCounter += 1
END

--Do inserts using GUIDs
DECLARE @GUIDTimeStart DateTime = GETDATE()
INSERT INTO TestGuid2 (SomeDate,batchNumber) 
SELECT adate,rowNum FROM #temp
DECLARE @GUIDTimeEnd  DateTime = GETDATE()

--Do inserts using IDENTITY
DECLARE @IdTimeStart DateTime = GETDATE()
INSERT INTO TestInt (SomeDate,rowNum FROM #temp
DECLARE @IdTimeEnd DateTime = GETDATE()

SELECT DATEDIFF(ms,@IdTimeStart,@IdTimeEnd) AS IdTime,@GUIDTimeStart,@GUIDTimeEnd) AS GuidTime

DROP TABLE TestGuid2
DROP TABLE TestInt
DROP TABLE #temp
GO

在阅读@ Martin的调查后,我在两种情况下都重新使用了建议的TOP(@num),即

...
--Do inserts using GUIDs
DECLARE @num INT = 2147483647; 
DECLARE @GUIDTimeStart DATETIME = GETDATE(); 
INSERT INTO TestGuid2 (SomeDate,batchNumber) 
SELECT TOP(@num) adate,rowNum FROM #temp; 
DECLARE @GUIDTimeEnd DATETIME = GETDATE();

--Do inserts using IDENTITY
DECLARE @IdTimeStart DateTime = GETDATE()
INSERT INTO TestInt (SomeDate,rowNum FROM #temp;
DECLARE @IdTimeEnd DateTime = GETDATE()
...

以下是时间结果:

Identity(s)  Guid(s)
       ---------    -----
       2.436        2.656
       2.940        2.716
       2.506        2.633
       2.380        2.643
       2.476        2.656
       2.846        2.670
       2.940        2.913
       2.453        2.653
       2.446        2.616
       2.986        2.683
       2.406        2.640
       2.460        2.650
       2.416        2.720

    -----------------------
Avg    2.426        2.688
StdDev 0.010        0.032

我无法获得实际的执行计划,因为查询永远不会返回!这似乎是一个bug. (运行Microsoft sql Server 2008 R2(RTM) – 10.50.1600.1(X64))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_no=&#39;LJCG001H&#39; THEN dbo.ELTPNAME(a.fw_nu) ELSE d.fm_name END),e.fw_state_nm,f.fw_rmk_nm
if not exists(select name from syscolumns where name=&#39;tod_no&#39; and id=object_id(&#39;iebo09d12&#39;)) alter table iebo09d12 add tod_no varchar(
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_nm,g.fa_name from LJSS007H a (nolock) Left join LJPA002H b (nolock) On a.pa_no =b.pa_no Left jo
要在 SQL Server 2019 中设置定时自动重启,可以使用 Windows 任务计划程序。下面是详细的步骤: 步骤一:创建批处理文件 打开记事本。 输入以下内容: net stop &quot;SQL Server (MSSQLSERVER)&quot; net start &quot;SQ
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的事务日志已满,导致数据库操作失败。要解决这个问题,可以按照以下步骤操作: 1. 备份事务日志首先,备份事务日志以释放空间: BACKUP LOG [EastRiver] TO DISK = N&#39;C:\Backup\East
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标识ID,若不知道怎么查询数据库的标识ID, 打开SQL Server management studio,点击工具。选择SQL Server Profiler。 登录,登录成功后,如果有个默认弹窗,先取消 新建追踪 命名
--最新的解决方法 --先创建用户帐户,不进行授权,然后通过下面的SQL语句将该用户帐户关联至对应的数据库用户。优点是避免了重新授权的操作。 USE tempdbEXEC sp_change_users_login &#39;Update_One&#39;, &#39;iemis&#39;, &#3
命令: ALTER TABLE 表名 add 列名 数据类型 default 默认值 not null 例如: ALTER TABLE LJEL005H add el_req int default 15 not null
declare @i int set @i=340 while @i&lt;415 begin set @i=@iʱ insert into LJWK007H select &#39;2024&#39;,&#39;28&#39;,&#39;9110&#39;,&#39;3PTSD621000000
alter table LJSU002H add default (0) for su_totalamt with values
命令: 有默认值时用:alter table 表名 DROP 约束 alter table 表名 DROP COLUMN 列名 例如: alter table LJEL005H DROP COLUMN el_req 注意: 如果有默认值先删除约束,否则报错: --消息 5074,级别 16,状态 1
mysql报错Unknown collation: utf8mb4_0900_ai_ci 解决方案: 将文件内的所有 utf8mb4_0900_ai_ci 换成 utf8_general_ci utf8mb4 换成 utf8
SQL SERVER根据数据表的某个栏位查询另一个数据表符合条件的某个栏位的值,如果多行则合并为一张字符串形式 要在 SQL Server 中根据一个数据表的某个列查询另一个数据表符合条件的某个列的值,并将多行结果合并为一个字符串,可以使用 FOR XML PATH 子句来执行此操作。以下是一个示例
ALTER TABLE LJPA001H DROP CONSTRAINT DF_LJPA001H_pa_sex_1 ALTER TABLE LJPA001H ALTER COLUMN pa_sex VARCHAR(1)
DATEDIFF和DATEADD函数。DATEDIFF函数计算两个日期之间的小时、天、周、月、年等时间间隔总数。DATEADD函数计算一个日期通过给时间间隔加减来获得一个新的日期。要了解更多的DATEDIFF和DATEADD函数以及时间间隔可以阅读微软联机帮助。使用DATEDIFF和DATEADD函
select top 100 substring(qr_code,8,8) ,* from [LiuJun_PKh_lcfc_hf] --where right(ri,8) or substring(qr_code,8,8)=. select top 10 left(right(one_code,1
1、发现事务日志备份突然停止了 2、查看维护计划中的事务日志设置 3、发现备份任务中,事务日志需要指向的数据库不在 4、进入数据库属性 5、在选项中将恢复模式改为“完整”
select DB_ID(&#39;SBTERPDB&#39;)
您收到的错误消息表明数据库 &#39;EastRiver&#39; 的事务日志已满,导致数据库操作失败。要解决这个问题,可以按照以下步骤操作: 1. 备份事务日志首先,备份事务日志以释放空间: BACKUP LOG [EastRiver] TO DISK = N&#39;C:\Backup\East