【问题标题】:Tables with no Primary Key没有主键的表
【发布时间】:2008-08-08 02:47:15
【问题描述】:

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

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

我的数据库是跨多个服务器进行合并复制的,因此我避开了 identity int 列,因为它们在复制中有点麻烦。

你的想法是什么?表应该有主键吗?或者,如果没有合理的列可以这样索引,那么没有任何聚集索引是否可以?

【问题讨论】:

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

标签: sql-server indexing


【解决方案1】:

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

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

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

罗伯

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 令人着迷。如果性能成为问题,我将研究“页面拆分和索引重建”选项。谢谢你。
    • 阅读 Kimberly Tripp 的 GUIDs as Primary and/or clustering keyDisk space is cheap - that's NOT the point! 以及更多她的优秀博客文章 - 她清楚地展示了 GUID 列上的集群键是多么糟糕的想法是。此外 - 热点是一个早已被揭穿的神话 - 在 SQL Server 6.5 之后的任何时候都不再是问题 ....
    【解决方案3】:

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

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

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

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

    【讨论】:

      【解决方案4】:

      主键有三个用途:

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

      前两个可以通过多种方式指定,就像您已经做过的那样。

      第三个理由好:

      • 供人类使用,因此他们可以轻松了解您的意图
      • 对于计算机,因此可能会比较或以其他方式处理您的表的程序可以在数据库中查询表的主键。

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

      【讨论】:

      • 将 guid 列作为主键绝对不是一个好主意,因为主键是聚集的,而 guid 是随机的。这意味着每当您插入新行时,您的表本质上都会在磁盘上进行重组。人们通常建议主键应该是连续的、不断增长的类型,以便将每个新行添加到表的末尾。
      • @MattHamilton re “...将 guid 列作为主键不是一个好主意,因为主键是聚集的,并且 guid 是随机的”来克服这个问题,您可以使用“newsequentialid( )" SQL 2005/2008 编辑中的函数:找到了必要的CodingHorror post 讨论这个;-)
      • 主键默认由聚集索引支持,但可以删除(聚集索引)。
      【解决方案5】:

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

      【讨论】:

      • 实际上,这是错误 - 正如 Kimberly Tripp(索引女王)清楚地表明:拥有 良好 聚集索引会增加 插入性能! sqlskills.com/BLOGS/KIMBERLY/post/…
      • 我不会称之为清楚地显示:) 她谈论的是一般原则,而不是用任何东西来支持她的陈述,而我谈论的是一个非常具体的我在实践中遇到的场景:将可能数亿条记录批量插入到非空表中,然后永远不会更新或以随机读取模式访问,而只会对其进行整体扫描。我想可能有比索引更多的因素在起作用。总是测试你的优化孩子。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          猜你喜欢
          • 2011-04-03
          • 2023-03-11
          • 1970-01-01
          • 2019-05-23
          • 2013-07-27
          • 1970-01-01
          • 2012-02-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多