SQL Server:为现有索引创建新的 GUID 值

ult*_*ife 6 index sql-server uniqueidentifier sql-server-2012 uuid

我有一个大约有 600 万行的现有表。相关栏目有:

ID int not null PK
Key uniqueidentifier not null
Run Code Online (Sandbox Code Playgroud)

通过Key查找对该表的读/写可能类似于 100 比 1。

现有的行都是使用创建的,newid()所以它们没有顺序。存在一个索引Key

CREATE NONCLUSTERED INDEX [idx_Robert] ON [dbo].[Aleksander]
(
    [Key] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON
    , FILLFACTOR = 80
    ) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

鉴于表中已经有数百万行,将 GUID 生成更改为顺序使用有什么好处newsequentialid()

展望未来,它将生成更好的数据,但由于不知道新的顺序 GUID 系列将在索引中的哪个位置,它会更快地破坏索引吗?将 GUID 生成保留为非顺序是否会更好,以便索引中的剩余页面空间更均匀地填充?

就表的增长率而言,它代表了大约3年的数据。

Year    NumRows
2012    3962660
2013    1661189
2014    711241
Run Code Online (Sandbox Code Playgroud)

在此问题中,2014 年已过半。在某些时候有一些代码更改降低了插入率,所以我相信 2013 年将是一个典型的年度行数。

Han*_*non 5

您可以通过移动到NEWSEQUENTIALID().

您目前每天向表中插入大约 4,500 行。该表目前约有 630 万行。这大约是每天表的 0.7%。假设表上的每次插入都会导致索引页拆分,这将导致每天向存储系统写入 9,000 页。这将为插入索引和重建/重组产生一些不必要的 I/O 压力。将Key字段切换到顺序 ID 生成将大大减少这种 I/O 模式。

如果切换到NEWSEQUENTIALID(),则INSERTs在表上执行的代码需要注意它会尝试插入重复项的可能性Key。这是一种遥远的可能性,但它仍然是一个可能性,是上是相同的NEWID(),并NEWSEQUENTIALID()-因为没有是由计算机生成的真正随机的。通过使索引唯一,对索引的查询可以获得一些好处,如下所示:

CREATE UNIQUE NONCLUSTERED INDEX [idx_Robert] ON [dbo].[Aleksander]
(
    [Key] ASC
) WITH (
    PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON
    , FILLFACTOR = 80
    ) ON [PRIMARY];
Run Code Online (Sandbox Code Playgroud)

此更改将使索引扫描的可能性降低。Paul White 在https://sqlkiwi.blogspot.com/2011/02/seeking-without-indexes.html 上有一篇关于此的优秀文章

由于您没有给出针对此表运行的查询类型的任何指示,因此我假设您在某些时候扫描整个索引与您的情况无关。如果您的数据确实存储在单个旋转磁盘上,那么让您的索引不分段应该可以减少查询返回信息所需的时间。

话虽如此,您可能会通过改进 IO 子系统获得更多的钱(时间就是金钱),因此不需要索引重建/重组。请参阅http://www.brentozar.com/archive/2012/08/sql-server-index-fragmentation/

如果您有兴趣测量您为该Key列使用的索引设置的效果,您可以使用系统 dmv, 监视索引使用的页面的页面空闲百分比(除其他外)sys.dm_exec_index_physical_stats。例如:

SELECT o.name, ps.avg_page_space_used_in_percent, ps.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(),OBJECT_ID('KeyTable','Table'), NULL, NULL, 'DETAILED') ps
    INNER JOIN sys.objects o ON ps.object_id = o.object_id;
Run Code Online (Sandbox Code Playgroud)

(您可能希望调整传递的参数以仅显示Key列上的索引。)

一旦avg_page_space_used_in_percent超过某个边界值,例如 95%(或更多),您就可以重建索引,这会对其进行碎片整理,并使用适当的FILL_FACTOR设置,在每个页面上为具有随机生成NEWID()值的新行留出一些空间。

如果您确实决定使用NEWSEQUENTIALID()索引,则可以使用FILL_FACTOR100% 的值,因为索引页将不再频繁拆分。这样做的好处是将保存索引所需的页数减少了 20%(相对于您的当前设置),从而减少了在必要时重建索引所需的 IO。

由于您每天仅以总表的 0.07% 左右的速度插入新行,因此系统可能需要很长时间才能自动触发对所涉及表的统计信息进行直方图更新。您可以使用该STATS_DATE()函数来确保统计信息永远不会超过x几天。最新的统计信息对于查询引擎生成最佳查询计划至关重要。


Gre*_*ker -3

“\xe2\x80\xa6 使用 newsequentialid() 将 GUID 生成更改为顺序生成有什么好处吗?”

\n\n

不。

\n\n

仅当 GUID 列上有聚集索引并且您希望避免插入导致的页面拆分时,顺序 GUID 才适用。

\n\n

编辑以解决以下评论:插入数据时,所有非聚集索引都会遭受页面拆分。例如,当您输入 Homer Simpson 的记录时,它会被输入到 LastName 索引的“S”叶页中,可能会导致页面拆分。但是,您不要求客户严格按字母顺序加入。

\n\n

此外,用于索引叶页和非叶页的锁存系统意味着页拆分比数据页上的页拆分需要更少的处理时间和资源。

\n\n

除此之外,OP 更改为顺序 id 需要什么?他们必须将 Key 列上的默认约束替换为 NEWSEQUENTIALID()。这不会影响表中的任何现有行(这很好,因为有外键使用这些键) - 只会影响新行。从那时起,插入的行将具有递增的键,但这些递增的键不一定会大于表中的现有数据(NEWSEQUENTIALID() 仅保证 GUID 大于 NEWSEQUENTIALID() 上生成的任何其他 GUID该计算机自重新启动以来)。这意味着谁的插入仍然会导致非聚集索引中的页面分裂!

\n

  • 显然,如果您插入“NEWID()”值,无论是否是聚集索引,索引都将受到页面拆分的影响。 (2认同)