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

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

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

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

有帮助吗?

解决方案

在处理索引时,您必须确定表的用途。如果您主要每秒插入 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 字段,如果不存在,它将添加一个。如果存在合适的字段,那么它将只使用那里的字段。

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

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

其他提示

主键有三个用途:

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

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

第三个理由也很好:

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

主键不必是自动递增数字字段,因此我认为将 guid 列指定为主键是一个好主意。

我只是插话,因为马特有点引诱我了。

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

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

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

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

主键不必是自动增量字段,在许多情况下,这仅仅意味着您使表结构变得复杂。

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

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

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

由于您正在进行复制,因此需要避免您的正确身份。我会让你的 GUID 成为主键,但非聚集键,因为你不能使用 newsequentialid。这让我觉得这是你最好的课程。如果你不将其设为PK而是在其上放置唯一索引,迟早可能会导致维护系统的人员无法正确理解FK关系,从而引入错误。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top