我试图了解顺序 guid 如何比常规 guid 表现更好。
是否因为使用常规 guid,索引使用 guid 的最后一个字节进行排序?由于它是随机的,它会导致很多碎片和页面拆分,因为它经常会将数据移动到另一个页面以插入新数据?
顺序 guid 正弦它是顺序的,它会导致更少的页面拆分和碎片吗?
我的理解正确吗?
如果有人可以对这个主题有更多的了解,我将不胜感激。
谢谢
编辑:
顺序 guid = NEWSEQUENTIALID(),
常规 guid = NEWID()
我试图了解顺序 guid 如何比常规 guid 表现更好。
是否因为使用常规 guid,索引使用 guid 的最后一个字节进行排序?由于它是随机的,它会导致很多碎片和页面拆分,因为它经常会将数据移动到另一个页面以插入新数据?
顺序 guid 正弦它是顺序的,它会导致更少的页面拆分和碎片吗?
我的理解正确吗?
如果有人可以对这个主题有更多的了解,我将不胜感激。
谢谢
编辑:
顺序 guid = NEWSEQUENTIALID(),
常规 guid = NEWID()
你在你的问题中已经说了很多。
使用顺序 GUID / 主键,新行将一起添加到表的末尾,这使得 SQL 服务器的事情变得容易。相比之下,随机主键意味着新记录可以插入到表中的任何位置——表的最后一页很有可能在缓存中(如果这是所有读取的地方),但是缓存中的表中间的随机页面相当低,这意味着需要额外的 IO。
最重要的是,在表格中间插入行时,可能没有足够的空间来插入额外的行。如果是这种情况,那么 SQL Server 需要执行额外的昂贵的 IO 操作以便为记录创建空间——避免这种情况的唯一方法是在数据之间散布间隙以允许插入额外的记录(称为填充因子),这本身会导致性能问题,因为数据分布在更多页面上,因此需要更多 IO 才能访问整个表。
我听从 Kimberly L. Tripp 在这个话题上的智慧:
但是,一个非连续的 GUID——比如它的值在客户端(使用 .NET)中生成或由 newid() 函数(在 SQL Server 中)生成的 GUID 可能是一个非常糟糕的选择——主要是因为碎片化它在基表中创建,但也因为它的大小。它不必要地宽(它比基于 int 的标识宽 4 倍——它可以为您提供 20 亿(实际上是 40 亿)个唯一行)。而且,如果您需要超过 20 亿行,您总是可以使用 bigint(8 字节 int)并获得 263-1 行。
为了可视化整个图片,可以使用名为ostress的工具。例如,您可以创建两个表:一个具有普通GUID 作为 PK,另一个具有顺序 GUID:
-- normal one
CREATE TABLE dbo.YourTable(
[id] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_YourTable] PRIMARY KEY NONCLUSTERED (id)
);
-- sequential one
CREATE TABLE dbo.YourTableSeq(
[id] [uniqueidentifier] NOT NULL CONSTRAINT [df_yourtable_id] DEFAULT (newsequentialid()),
CONSTRAINT [PK_YourTableSeq] PRIMARY KEY NONCLUSTERED (id)
);
然后使用给定的实用程序,您可以运行一些插入,并选择有关索引碎片的统计信息:
ostress -Slocalhost -E -dYourDB -Q"INSERT INTO dbo.YourTable VALUES (NEWID()); SELECT count(*) AS Cnt FROM dbo.YourTable; SELECT AVG_FRAGMENTATION_IN_PERCENT AS AvgPageFragmentation, PAGE_COUNT AS PageCounts FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, N'LIMITED') DPS INNER JOIN sysindexes SI ON DPS.OBJECT_ID = SI.ID AND DPS.INDEX_ID = SI.INDID WHERE SI.NAME = 'PK_YourTable';" -oE:\incoming\TMP\ -n1 -r10000
ostress -Slocalhost -E -dYourDB -Q"INSERT INTO dbo.YourTableSeq DEFAULT VALUES; SELECT count(*) AS Cnt FROM dbo.YourTableSeq; SELECT AVG_FRAGMENTATION_IN_PERCENT AS AvgPageFragmentation, PAGE_COUNT AS PageCounts FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, N'LIMITED') DPS INNER JOIN sysindexes SI ON DPS.OBJECT_ID = SI.ID AND DPS.INDEX_ID = SI.INDID WHERE SI.NAME = 'PK_YourTableSeq';" -oE:\incoming\TMP\ -n1 -r10000
然后在文件 E:\incoming\TMP\query.out 中,您将找到您的统计信息。我的结果是:
"Normal" GUID:
Records AvgPageFragmentation PageCounts
----------------------------------------------
1000 87.5 8
2000 93.75 16
3000 96.15384615384616 26
4000 96.875 32
5000 96.969696969696969 33
10000 98.571428571428584 70
Sequential GUID:
Records AvgPageFragmentation PageCounts
----------------------------------------------
1000 83.333333333333343 6
2000 63.636363636363633 11
3000 41.17647058823529 17
4000 31.818181818181817 22
5000 25.0 28
10000 12.727272727272727 55
正如您所看到的,在插入顺序生成的 GUID 时,索引的碎片要少得多,因为插入操作导致新的页面分配更少。