4

我有一个表,其主键作为聚集的 GUID 字段;我正在使用 NEWSEQUENTIALID()而不是生成 GUID NEWID。不幸的是,因为这个表每天看到大约 25k-100k 的插入,在几个小时内(默认:聚集)主键索引变成了 99% 的碎片。

我最初使用NEWID而不是生成顺序 ID,但即使我重新创建表并使用重新插入所有行NEWSEQUENTIALID(并将其指定为主键列的默认值),我仍然在几个小时。(该表目前有大约 130 万条记录。

我曾考虑用整数主键替换 GUID,但我不确定这是否可行;另外,由于我们的团队使用 GUID 作为主键而不是整数,所以我认为我没有足够的支持来做这件事。

我有什么办法可以让这个东西进行碎片整理?我使用的是 SQL Server Express,因此我无法访问 SQL 代理(因此无法定期运行维护计划来重建索引)。

我也很可能在将来的某个时候拆分这个数据库/表(因为数据量大),所以我可能需要 GUID 来合并表。

另外:我不能使用索引视图,因为我有一个内部选择,这对我来说很难展开到一个连接中。

4

4 回答 4

7

以我自己的个人经验,将GUIDs 作为您的集群键可以对您的系统产生重大的积极影响 - 尤其是在索引碎片方面!

我的新INT IDENTITY聚类索引几乎没有任何碎片——即使经过数月的密集日常生产使用。绝对值得!!

在 SQL Server 中使用Guid数据类型作为集群键是一个非常糟糕的选择——无论你如何看待它......

请参阅有关该主题的 Kimberly Tripp(索引女王)的一些博客文章:

以及她关于集群键主题的任何其他博客......

于 2012-01-17T14:49:34.160 回答
2

我完全意识到我正在删除一个 8 年前(撰写本文时)开始的线程,但是对 NEWID()、NEWSEQUENTIALID()、“不断增加的整数”和我简单地将其称为“Exp Ansive Updates”(带有“A”),它实际上是 Exp E nsive(带有“E”)。

让我们先讨论后者,这可能是 OP 遇到的真正问题......

只有微小的差异,当涉及到不必要的页面拆分创建和由此产生的碎片时,这并不重要,NEWSEQUENTIALID 和“不断增加的 INT”都以相同的方式工作......它们本身只创建“好”的页面拆分(这也是“坏的”,但这是另一个讨论的主题)。因此,参考最初发布的问题,Op 表示从完全随机的 NEWID 切换到“不断增加的”NEWSEQUENTIALID 似乎对正在创建的碎片量没有影响。

原因不是 NEWSEQUENTIALID 有问题(它没有)。碎片问题很可能是正在插入新行(这将导致 NEWSEQUENTIALID 没有碎片),然后这些新行会受到另一个进程来更新它们。如果更新是“Exp Ansive”,其中一行中的某些可变宽度列变得更宽,那么这将导致大量页面拆分。即使您使用相当低的填充因子构建索引也会发生这种情况,因为插入不会停止插入页面,只是因为它们到达填充因子。相反,大量插入将插入到页面中,直到它们几乎 100% 填满(取决于每页的行数,这取决于插入的行的宽度),然后使用 "好”的页面拆分几乎没有碎片,就像您使用的是一个不断增加的整数一样。

因此,您将所有这些行插入到连续的页面中,它们会被填充到尽可能接近 100% 的位置。一切都很好......没有碎片。但是随后您执行“插入后处理”来更新您刚刚插入的行。如果行的大小由于“Exp Ansive”而增加,那么KAAAA-BOOOOOM !!! 所有这些完全完整的页面最终都会分裂。

这种扩展的最常见来源之一是人们使用“穷人的审计”并且他们有一个“Modified_BY”列,该列从 NULL 变为某个值。有很多方法可以解决这个特定问题,但同样,远远超出了这个线程和帖子的范围。

将齿轮切换到由 NEWID() 生成的随机 GUID... 有很多不使用它们的理由,但是,完全与你所相信的相反,碎片实际上不是其中之一。我已经以非常“爱丽丝的餐厅时尚”(大量图形和图形上的符号)的方式进行了几次演示,证明了这一点。为了做出适合这篇文章的超过 1 小时的演示,我会告诉你,这一切都归结为人们不断犯的几个小但致命的错误......

  1. 他们继续使用 REORGANIZE 因为它被认为是“最佳实践”是主要问题。他们没有意识到 REORGANIZE 实际上并不能像他们认为的那样对 GUID 起作用。它没有在页面上提供额外的空间,而是实际上删除了额外的空间,而且,我的索引管理员伙伴,实际上使 GUID 的碎片化。在随机指南上进行索引维护时,您不得使用重组!时期!!!即使您使用的是 Express 或 Standard Edition,也不会。如果您没有时间、资源或磁盘空间来重建它们,那么实际上最好不要对随机 GUID 进行任何索引维护,而不是使用 REORGANZE 来做错事。等到您可以进行重建。

  2. 您必须在随机 GUID 键控索引上设置较低的 FILL FACTOR。将它们留在“0”几乎与重组它们一样糟糕。当然,取决于索引的行有多宽,每天插入多少行,以及您希望在随机 GUID 上使用绝对零页面拆分(甚至不应该是“好”的!!!!)多长时间索引,我告诉人们将他们的 FILL FACTOR 设置为 71、81 或 91。我将所有这些都以“1”结尾的原因是因为当“ExpAnsive”更新时您需要为随机 GUID 修复最后一件事不存在,这是下面的第 3 项。

  3. 您必须每天晚上检查基于随机 GUID 的索引。我选择给他们所有以“1”结尾的填充因子的原因是因为这就是你正在寻找的逻辑碎片的百分比。只要它们超过 1% 标记,您就必须重新构建它们,因为几乎整个索引中的每一页都处于将要拆分的位置。(我称这些为“低阈值重建”)。现在,不要混淆。如果一切设置正确并且没有“ExpAnsive”更新,那么您的 GUID 键控聚集索引可以持续数周而没有分页或相关碎片,而您的更窄的非聚集索引可以在几个月内完全没有碎片!

当然,另一个大错误是“ExpAnsive”更新。那些会杀死几乎所有东西,但令人惊讶的是,随机 GUID 实际上会比使用上述相同步骤的大多数其他东西更好地经受住这样的冲击。

您真正需要做的是修复“ExpAnsive”更新,使它们不再是“ExpAnsive”。就像我说的那样,这是一个完整的主题,对这篇文章很渴望。

于 2020-04-22T23:13:16.507 回答
1

这是具有大量插入的 Guid 索引的预期行为。大多数情况下,您选择 guid 作为键只是因为记录是由多个来源生成的,并且您需要使各个来源不会互相影响。这里的一个例子是离线移动设备。现场工作人员需要在未连接时创建新记录,因此移动设备可以安全地创建以 guid 为键的记录。稍后重新联机时,设备可以安全地与数据库同步,而无需担心任何密钥冲突。

如果您在单个服务器上生成 guid,通常最好使用简单的标识列。如果您真的想要这些指南,您仍然可以包含它们……您可能需要三思而后行将它们用于您的聚集索引。您可能希望在 guid 上进行集群的唯一原因是,如果稍后您将返回表并根据它的 guid 一次查询一条记录。您看到的插入率似乎不太可能。但是,如果是这种情况,您可以通过降低索引的填充因子来帮助缓解问题。这将增加使用的磁盘空间量(并意味着以后有更多的磁盘寻道),但页面会更快地填满,并且您将避免一些索引重新洗牌。

如果基于整数的键是不可能的,那么在此处查看的另一个选项是顺序 guid。这仍然提供了独特性,同时也减少了碎片。

于 2012-01-17T14:51:13.490 回答
-2

在 stackexchange 数据浏览器中查看这个简单的查询。它看起来newsequentialid()增加了 guid最重要的部分,而不是最不重要的部分。这可能是您看到的碎片的一个可能原因。

如果您必须使用 guid,或许可以考虑通过代码生成它们并在您的插入语句中发送它们,而不是依赖数据库生成它们。使用“comb”技术,将当前时间戳用作 guid 的一部分,以最低有效数字递增。


编辑

..或者如果您不想在代码中生成它们,您可以在数据库中执行类似的操作

CAST(CAST(NEWSEQUENTIALID() AS BINARY(10)) + CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

作为您的默认值,根据对上述查询的此修改

于 2012-01-17T14:18:23.987 回答