41

我有几个表,其唯一的唯一数据是唯一标识符(Guid)列。因为 guid 是非顺序的(并且它们是客户端生成的,所以我不能使用 newsequentialid()),所以我在这个 ID 字段上创建了一个非主索引、非聚集索引,而不是给表一个聚集主索引钥匙。

我想知道这种方法对性能的影响是什么。我见过一些人建议表应该有一个自动递增(“身份”)int作为集群主键,即使它没有任何意义,因为这意味着数据库引擎本身可以使用该值快速查找一行,而不必使用书签。

我的数据库在一堆服务器上进行了合并复制,所以我避开了 identity int 列,因为它们在复制中有点麻烦。

你怎么认为?表应该有主键吗?或者,如果没有合理的列以这种方式建立索引,那么没有任何聚集索引是否可以?

4

7 回答 7

33

在处理索引时,您必须确定您的表将用于什么。如果您主要是每秒插入 1000 行而不进行任何查询,那么聚集索引会影响性能。如果您每秒执行 1000 次查询,那么没有索引将导致性能非常差。尝试调整查询/索引时,最好的办法是使用 SQL Server 中的查询计划分析器和 SQL 探查器。这将向您显示您在哪里遇到了代价高昂的表扫描或其他性能障碍。

至于 GUID 与 ID 的争论,你可以在网上找到同时发誓的人。我一直被教导要使用 GUID,除非我有充分的理由不这样做。Jeff 有一篇很好的文章讨论了使用 GUID 的原因:https ://blog.codinghorror.com/primary-keys-ids-versus-guids/ 。

与大多数与开发相关的事情一样,如果您希望提高性能,那么没有一个单一的正确答案。这实际上取决于您要完成的工作以及您如何实施解决方案。唯一真正的答案是针对性能指标进行测试、测试和再次测试,以确保您实现目标。

[编辑] @Matt,在对 GUID/ID 辩论进行了更多研究之后,我发现了这篇文章。就像我之前提到的,没有正确或错误的答案。这取决于您的具体实施需求。但这些是使用 GUID 作为主键的一些非常正当的理由:

例如,有一个称为“热点”的问题,其中表中的某些数据页面处于相对较高的货币争用状态。基本上,发生的情况是表上的大部分流量(以及因此页级锁)发生在表的一小块区域,接近末尾。新记录总是会转到这个热点,因为 IDENTITY 是一个序列号生成器。这些插入很麻烦,因为它们需要在它们添加到的页面(热点)上进行排他性页面锁定。由于页面锁定机制,这有效地将所有插入序列化到表中。另一方面,NewID() 不受热点的影响。使用 NewID() 函数生成的值仅对于短时间的插入是连续的(函数被非常快速地调用,例如在多行插入期间),

此外,由于插入是随机分布的,因此页面拆分的可能性大大降低。虽然页面拆分在这里并没有太糟糕,但效果确实很快就会增加。使用 IDENTITY,页面填充因子作为一种调整机制非常无用,最好设置为 100% - 行将永远不会插入到最后一个页面之外的任何页面中。使用 NewID(),您实际上可以将填充因子用作提高性能的工具。您可以将填充因子设置为近似于索引重建之间的估计卷增长的水平,然后使用 dbcc reindex 在非高峰时段安排重建。这有效地将页面拆分的性能影响延迟到非高峰时间。

如果您甚至认为您可能需要为有问题的表启用复制 - 那么您不妨将 PK 设为唯一标识符并将 guid 字段标记为 ROWGUIDCOL。复制将需要具有此属性的唯一值 guid 字段,如果不存在,它将添加一个。如果存在合适的字段,那么它将只使用那里的那个。

使用 PK 的 GUID 的另一个巨大好处是,该值确实保证是唯一的——不仅在服务器生成的所有值中,而且在所有计算机生成的所有值中——无论是您的数据库服务器、Web 服务器、应用程序服务器,或客户端计算机。现在几乎每一种现代语言都具有生成有效 guid 的能力——在 .NET 中,您可以使用 System.Guid.NewGuid。这在处理缓存的主从数据集时非常方便。您不必使用疯狂的临时键控方案来在提交记录之前将它们关联起来。您只需在创建记录时为每个新记录的永久键值从操作系统获取一个完全有效的新 Guid。

http://forums.asp.net/t/264350.aspx

于 2008-08-08T03:04:29.047 回答
7

主键有三个用途:

  • 表示列应该是唯一的
  • 表示列应该是非空的
  • 记录这是行的唯一标识符的意图

正如您已经完成的那样,可以通过多种方式指定前两个。

第三个理由很好:

  • 为人类,所以他们可以很容易地看到你的意图
  • 对于计算机,因此可能会比较或以其他方式处理您的表的程序可以查询数据库以获取表的主键。

主键不必是自动递增的数字字段,所以我想说将您的 guid 列指定为主键是个好主意。

于 2008-08-08T03:04:55.037 回答
7

只是跳进去,因为马特有点引诱我。

您需要了解,虽然聚集索引默认放在表的主键上,但这两个概念是分开的,应该分开考虑。CIX 指示 NCIX 存储和引用数据的方式,而 PK 为每一行提供唯一性以满足表的逻辑要求。

没有 CIX 的表只是一个堆。没有 PK 的表通常被认为是“不是表”。最好分别了解 PK 和 CIX 概念,以便您在数据库设计中做出明智的决策。

于 2009-08-05T04:51:00.373 回答
3

没有人回答实际问题:没有 PK 或 CLUSTERED 索引的表的优点/缺点是什么。在我看来,如果您优化更快的插入(尤其是增量批量插入,例如当您将数据批量加载到非空表中时),这样的表:没有聚集索引,没有约束,没有外键,没有默认值和在具有简单恢复模型的数据库中,没有主键是最好的。现在,如果您想查询此表(而不是完整地扫描它),您可能需要根据需要添加一个非聚集的非唯一索引,但将它们保持在最低限度。

于 2010-03-19T18:17:12.213 回答
0

我也一直听说有一个自动递增的 int 对性能有好处,即使你实际上并没有使用它。

于 2008-08-08T03:00:36.810 回答
0

主键不必是自动递增字段,在许多情况下,这只是意味着您正在使表结构复杂化。

相反,主键应该是唯一标识元组的最小属性集合(请注意,大多数 DBMS 将允许复合主键)。

用技术术语来说,它应该是元组中所有其他字段在功能上完全依赖的字段。(如果不是,您可能需要正常化)。

在实践中,性能问题可能意味着您合并表并使用递增字段,但我似乎记得过早优化是邪恶的......

于 2008-08-08T06:25:50.030 回答
0

由于您正在进行复制,因此您的正确身份是需要清除的。我会让你的 GUID 成为主键,但不是聚集的,因为你不能使用 newsequentialid。这让我觉得你最好的课程。如果你不把它做成一个PK,而是在它上面加上一个唯一的索引,迟早可能会导致维护系统的人不理解FK关系正确引入错误。

于 2010-11-03T18:20:12.173 回答