Search code examples
sqlsql-serverindexingioclustered-index

INSERTs with sequential GUID key on clustered index not significantly faster


In SQL Server 2008 I have tried to reproduce the results from the experiments on clustered index on sequential vs. non-sequential GUID keys seen here http://sqlblog.com/blogs/denis_gobo/archive/2009/02/05/11743.aspx but I do not experience the significant speedup for insertions that I would expect (and the author experiences). The page utilization is clearly improved with the sequential GUID, but for some reasons, inserting 10,000 rows is only around 100 ms faster (out of 10,300 ms).

I use the following code:

CREATE TABLE TestGuid1 (Id UNIQUEIDENTIFIER not null DEFAULT newid(),
SomeDate DATETIME, batchNumber BIGINT)

CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER not null DEFAULT newsequentialid(),
SomeDate DATETIME, batchNumber BIGINT)

CREATE CLUSTERED INDEX ix_id1 ON TestGuid1(id)
CREATE CLUSTERED INDEX ix_id2 ON TestGuid2(id)

go

SET NOCOUNT ON
INSERT TestGuid1 (SomeDate,batchNumber) VALUES (GETDATE(),3)
go 10000

SET NOCOUNT ON
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),3)
go 10000

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

SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate))
FROM TestGuid1
GROUP BY batchNumber

SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate))
FROM TestGuid2
GROUP BY batchNumber

Can anyone explain why I do not experience a more significant speedup for insertions on TestGuid2?

Follow-up: As requested in the thread below, I have expanded the test: the test results tend to vary significantly over time, so now the experiments are repeated N times, and the total and average time usage reported. I have also added a third test, namely for primary keys on sequential integer columns. This should be the fastest and most compact of all three methods as the integer type is smaller and IDENTITY(1,1) is (or at least should be) fast. At least by my intuition. The average execution time is now to the benefit of the sequential GUID, but surprisingly insertions in the third experiment (with sequential integer keys) is slower than sequential GUIDs. I have no explanation of this. Here is the code for the new experiments:

SET NOCOUNT ON

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

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,
SomeDate DATETIME, batchNumber BIGINT, 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 TestGuid1 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 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,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

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

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

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

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

DROP TABLE TestGuid1
DROP TABLE TestGuid2
DROP TABLE TestInt

And the average execution times:

NEWID()            3064
NEWSEQUENTIALID()  1977
IDENTITY()         2223

The page usage is as follows:

Table          Pages  AveragePageDensity
----------------------------------------
TestGuid1      50871  68,4
TestGuid2      35089  99,2
TestInt        32259  98,7

I fail to see, why these page statistics (which are best for TestInt) doesn't imply that experiment three is fastest.


Solution

  • Can you try this modified script and post your results?

        SET NOCOUNT ON
    
        CREATE TABLE TestGuid1 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
        SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
    
        CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
        SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
    
        DECLARE @BatchCounter INT = 1
    
        WHILE (@BatchCounter <= 20)
        BEGIN 
        BEGIN TRAN
    
        DECLARE @LocalCounter INT = 0
    
            WHILE (@LocalCounter <= 100000)
            BEGIN
            INSERT TestGuid1 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
            SET @LocalCounter +=1
            END
    
        SET @LocalCounter = 0
    
            WHILE (@LocalCounter <= 100000)
            BEGIN
            INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
            SET @LocalCounter +=1
            END
    
        SET @BatchCounter +=1
        COMMIT 
        END
    
        DBCC showcontig ('TestGuid1') WITH tableresults
        DBCC showcontig ('TestGuid2')  WITH tableresults
    
        SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWID()]
        FROM TestGuid1
        GROUP BY batchNumber
    
        SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
        FROM TestGuid2
        GROUP BY batchNumber
    
    DROP TABLE TestGuid1
    DROP TABLE TestGuid2
    

    I see quite wildly varying results between individual runs (on my laptop not a server!) but a definite trend for sequential to be faster.

    NEWID() Average 5168.9

    batchNumber          NEWID()
    -------------------- -----------
    1                    4270
    2                    2480
    3                    2706
    4                    3333
    5                    7480
    6                    5346
    7                    4306
    8                    7713
    9                    7313
    10                   4760
    11                   4680
    12                   4113
    13                   3433
    14                   2686
    15                   4963
    16                   8040
    17                   5313
    18                   8160
    19                   9533
    20                   2750
    

    NEWSEQUENTIALID() Average 3000.85

    batchNumber          NEWSEQUENTIALID()
    -------------------- -----------------
    1                    2016
    2                    1820
    3                    1886
    4                    1870
    5                    4873
    6                    3473
    7                    3730
    8                    3690
    9                    1983
    10                   2020
    11                   1906
    12                   5596
    13                   2100
    14                   1950
    15                   2096
    16                   1876
    17                   5196
    18                   2110
    19                   2113
    20                   7713