没有主键的表

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

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

我的数据库在一堆服务器上被合并复制,所以我已经避开了身份int列,因为它们有点复杂。

你怎么看?表格是否有主键?或者没有任何聚集索引,如果没有明智的列索引这种方式?

0
既然你正在做复制,你的身份是正确的,所以要避开。因为你不能使用newsequentialid,所以我会让你的GUID成为主键但是非聚簇。这使我成为你最好的课程。如果你没有把它做成一个PK,但在它上面添加一个唯一的索引,迟早可能会导致维护该系统的人不能正确理解FK关系引入错误。
额外 作者 HLGEM,

7 答案

主键有三个目的:

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

前两个可以用很多方式指定,就像你已经完成的那样。

第三个原因是好的:

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

主键不一定是自动递增的数字字段,所以我会说,把你的guid列指定为主键是个好主意。

0
额外
@MattHamilton重新“...不是一个好主意,有一个guid列作为主键,因为主键集群和guid是随机的”来克服这一点,你可以使用SQL 2005/2008上的“newsequentialid()”函数编辑:找到必要的 CodingHorror文章,谈论这个;-)
额外 作者 Leon Bambrick,
将guid列作为主键肯定不是一个好主意,因为主键是聚簇的而guid是随机的。这意味着,只要你插入一个新行,你的表本质上就是在磁盘上进行重构。人们通常建议主键应该是连续的,不断增长的类型,以便每个新的行都被添加到表的末尾。
额外 作者 Matt Hamilton,
主键默认由聚集索引支持,但可以删除(聚集索引)。
额外 作者 Andrei Rînea,

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

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

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

在实践中,性能问题可能意味着你合并表,并使用增量字段,但我似乎回想起过早优化是邪恶的东西......

0
额外

我也总是听说自动递增int对于性能是有好处的,即使你没有真正使用它。

0
额外

既然你正在做复制,你的身份是正确的,所以要避开。因为你不能使用newsequentialid,所以我会让你的GUID成为主键但是非聚簇。这使我成为你最好的课程。如果你没有把它做成一个PK,但在它上面添加一个唯一的索引,迟早可能会导致维护该系统的人不能正确理解FK关系引入错误。

0
额外

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

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

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

0
额外

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

0
额外
实际上,这是错误 - 因为Kimberly Tripp(索引女王)清楚地表明:拥有好的聚集索引将会增加 INSERT性能! sqlskills.com/BLOGS/KIMBERLY/post/…
额外 作者 marc_s,
我不会那么清楚地表明:)她谈论的是一般原则,而不是支持她的陈述,当我谈论我在实践中遇到的一个非常具体的场景时:将数以百万计的记录批量插入非空表中,然后该表不会以随机读取模式更新或访问,而只是完全扫描。尽管如此,我认为可能还有更多因素在起作用。总是测试你的优化孩子。
额外 作者 zvolkov,

在处理索引时,你必须确定你的表格将用于什么。如果你主要每秒插入1000行而不做任何查询,那么聚集索引对性能的影响就很大。如果您每秒钟处理1000个查询,那么没有索引会导致非常糟糕的性能。尝试调整查询/索引时最好的做法是使用SQL Server中的查询计划分析器和SQL事件探查器。这将向您显示您正在进入昂贵的表扫描或其他性能障碍者的位置。

至于GUID和ID参数,你可以在网上找到两个人发誓的人。我总是被教导使用GUID,除非我有一个非常好的理由不这样做。 Jeff有一篇很好的文章,谈论使用GUID的原因: http://www.codinghorror .COM /博客/档案/ 000817.html

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

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

例如,有一个被称为“热点”的问题,表中的某些数据页面处于相对较高的货币争用状态。基本上,会发生什么情况是表中的大部分流量(以及因此页面级锁)发生在表的一小部分区域上,到最后。新纪录总是会进入这个热点,因为IDENTITY是一个序号发生器。这些插入是很麻烦的,因为它们需要在它们被添加到的页面(热点)上进行Exlusive页面锁定。由于页面锁定机制,这将所有插入有效地串行化到表格。另一方面,NewID()不会受到热点的影响。使用NewID()函数生成的值仅对于短插入插入(其中函数被快速调用,例如在多行插入期间)是连续的,这导致插入的行在整个表的数据页中随机传播在最后 - 因此消除了插入热点。     另外,因为插入是随机分布的,页面拆分的机会大大减少。虽然页面在这里和那里分裂并不是太糟糕,但效果会很快加起来。使用IDENTITY,页面填充因子作为调整机制是相当无用的,并且可能被设置为100% - 行将永远不会插入任何页面,而是最后一行。使用NewID(),您实际上可以将填充因子用作性能支持工具。您可以将填充因子设置为接近索引重建之间的估计增长量的级别,然后使用dbcc reindex在非高峰时段安排重建。这有效地延迟了页面拆分的性能命中,直到非高峰时间。     

如果您甚至认为您可能需要为相关表启用复制功能 - 那么您也可以使PK成为uniqueidentifier并将guid字段标记为ROWGUIDCOL。复制将需要一个具有此属性的唯一值GUID字段,如果不存在,它将添加一个。如果存在合适的字段,那么它只会使用那里的那个。

     使用GUIDs的PK的另一个巨大好处是事实上这个值确实是唯一的 - 不仅仅是由这个服务器产生的所有值,而是由所有电脑 - 无论是你的数据库服务器,网络服务器,应用服务器还是客户机。几乎每种现代语言都有能力生成有效的GUID - 在.NET中,您可以使用System.Guid.NewGuid。这在处理缓存的主数据集时尤其方便。您不必采用疯狂的临时密钥方案,只是在您的记录被提交之前将您的记录关联在一起。您只需在创建记录时从操作系统获取每个新记录的永久性键值的完全有效的新Guid。      

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

0
额外
迷人。如果性能成为问题,我将研究“页面拆分和索引重建”选项。感谢那。
额外 作者 Matt Hamilton,
阅读Kimberly Tripp的作为主要的GUID和/或集群密钥磁盘空间很便宜 - 这是不是<�!b>重点!,还有很多她的优秀博客文章 - 她清楚地表明了一个想法在GUID上的集群密钥有多糟糕列是。另外 - 热点是一个长期被揭穿的神话 - 在SQL Server 6.5之后不再是问题....
额外 作者 marc_s,