SQL Server数据库中有多个“ID”列?

6
通过此链接,我知道GUID不适合作为聚集索引,但它可以在任何地方唯一创建。对于某些高级 SQL Server 功能(如复制等),它是必需的。
如果我想要将GUID列作为典型的主键,这是否被认为是糟糕的设计?还有,这假设我的聚集 ID 需要一个单独的 int 标识列,并且作为额外的奖励来获取一个“用户友好”的ID?
更新
在查看您的反馈后,我意识到我没有正确表达我的问题。我知道 GUID 可以成为良好的(即使过度)主键,但是聚类索引不好(总体上)。我的问题更直接地问,添加第二个 "int identity" 列作为聚类索引是否糟糕?
我想,GUID 将是 PK,并使用它来构建所有关系、连接等。然后,我会添加一个额外的 "ID" 作为聚簇索引,而不是使用自然键。我想知道这是否糟糕?

2
不,绝对不是这样的!如果您必须使用GUID作为主键,则将聚集键与主键分离是一个好选择。这样做可以节省很多性能上的麻烦 - 相信我。 - marc_s
8个回答

2
如果您一定要创建标识字段,请将其用作主键。考虑查询此数据。在连接时,整数更快且更容易在编写查询时指定。
如果必须进行复制,请使用GUID,但不要将其用作主键。

你认为速度会有很大的差别吗?考虑到这个应用程序可能不会超过整个数据库的200k记录。 - Nate

1

使用GUID是懒惰的做法——也就是说,数据库管理员无法正确地对其数据进行建模。此外,它提供了非常糟糕的连接性能——通常是(16字节类型,局部性差)。

如果我想将GUID列作为我的典型主键,并且使用单独的int标识列作为我的聚集ID,以及一个“用户友好”ID,这是一种不好的设计吗?

是的,这是非常糟糕的——首先,您不希望在表中有多个“人工”候选键。其次,如果您想要一个用作键的用户友好ID,只需使用固定长度类型,例如char[8]或binary(8)——最好使用二进制,因为排序不会使用语言环境;您可以使用16字节类型,但是您会注意到性能下降——但不像GUID那样糟糕。您可以使用这些固定类型来构建自己的用户友好分配方案,该方案保留一些局部性,但生成明智和有意义的ID。


举个例子:

如果你正在编写某种类型的CRM系统(比如在线保险报价),并且你想要一个极其用户友好的类型,例如一个看起来像这样的保险报价参考号(QR):“AD CAR MT 122299432”。

在这种情况下——由于引用长度巨大——我会创建一个单独的LUT /符号表来解析引用参考到实际使用的标识符。但是,我将把这个LUT与模型的其余部分分开,我永远不会在模型中的任何其他地方使用引用参考,特别是不会在代表QR的表格中使用。

Create Table QRLut
{
    bigint bigint_id;
    char(32) QR;
}

现在如果我的模型有一个代表QR的表格和其他20个特征bigint QR作为外键的表格——使用bigint将使我的数据库能够良好扩展——连接谓词越宽,对内存总线造成的争用就越多——内存总线上的争用量决定了您的CPU可以被饱和的程度(多个CPU)。

您可能会认为在实际代表行情的表格中仅放置易于使用的QR即可,但请记住,SQL服务器会收集表格和索引的统计信息,而您不希望让服务器基于用户友好的QR(因为它太大并浪费空间)做出缓存决策。


那么你会将int/bigint用作主键和聚集索引吗?并完全放弃Guid吗? - Nate
是的,如果我需要某种用户友好的 ID(在您的情况下是 GUID),我会使用辅助表来实现。 - Hassan Syed

1
你打算通过 GUID 实现什么目标?int identity列在该表中也将是唯一的。你实际上需要或者预期需要复制的能力吗?如果需要,使用GUID是否真的比通过 identity range mangement options 处理标识列更可取?
如果你喜欢使用 Active Record 模式生成的“漂亮”的 ID,则我认为你应该尝试使用它而不是 GUID。如果你确实需要复制,则使用适合标识列的复制策略之一。

我所谓的“pretty”只是指对于进行查询的用户来说,键入的内容较少。我意识到我可能只需要使用 int 作为我的主键,但我喜欢使用 GUID 作为主键的想法,因为它能够在未来提供最大的灵活性。这样做是错误的吗? - Nate

1

考虑仅使用GUID,但使用NEWSEQUENTIALID方法获取GUID(该方法分配顺序值,因此不会像NEWID方法那样具有相同的聚集性能问题)。

使用二级INT键作为索引的问题在于,如果它是一个足够的索引,为什么还要使用GUID?如果需要GUID,如何使用INT索引?我不确定您是否需要GUID,如果需要,为什么需要:您是否在多个数据库之间进行复制和/或合并?如果确实需要GUID,则尚未指定在该场景中如何使用非全局唯一INT索引。


听起来你的意思是我没有为使用Guid做出充分的理由,我同意这是过度设计,但我的问题是它是否过度设计了太多?
如果您有使用情况(例如多个数据库),并且可以容忍仅使用更大(16字节)键导致每页内存较少索引实例的线性O(1)性能损失,则使用GUID代替INT作为主键很方便。
更大的担忧是使用(随机)GUID可能会影响聚集时的性能。为了抵消这种影响:
要么使用其他内容(例如记录的自然键)作为聚集索引,即使您仍然使用GUID作为主键;
要么让聚集索引与GUID主键相同,但使用NewSequentialId()而不是NewId()来分配GUID值。
“如果我不确定是否有一个良好的自然ID,可以将额外的人工“ID”用于聚类分析,这会是一件坏事吗?” 我认为,您为什么不使用带有NewSequentialId()的GUID来代替呢?它恰好可以用于此目的。”

2
如果你打算使用 GUID,一定要这样做。但在我看来,GUID仍然不是一个好的解决方案。 - Hassan Syed
1
阅读我的帖子,了解一些讨论——GUID是地址空间的浪费——也就是说,它们从随机位置开始,并且你无法控制用于生成它们的方案。 - Hassan Syed
1
@Chris 我明白,我认为现在只有30%的数据库工作需要严格的建模注意。但是对于你可能感兴趣的工作量的30%,你可能会对这项研究第16页和第17页上的图表感兴趣:“每个程序员都应该了解的内存知识”http://people.redhat.com/drepper/cpumemory.pdf-不良局部性和2倍(与bigint相比)或4倍(与int相比)的大小=悲伤的内存总线:D - Hassan Syed
@Hassan 我使用自然键作为聚集索引。例如,假设员工有一个GUID作为他们的ID,并且每个员工属于一个部门,并且通常使用SELECT检索部门内的所有员工:因此,我将DepartmentId定义为聚集索引,即使EmployeeId是主键。 - ChrisW
是的,通过将其设置为默认条目,让数据库分配它是最明显的使用方式。我认为这也是通常分配整数IDENTITY值的方式:它们不是由业务层分配的,而是业务层向数据库插入新记录时使用NULL主键,并让数据库分配主键值。 - ChrisW
显示剩余5条评论

0

这并不是一个糟糕的设计,为您的聚集键使用 int Identity 可以带来许多好处(窄、唯一、升序),同时保持 GUID 的功能目的非常分离,并充当您的主键。

如果有什么问题,我建议您采取正确的方法,尽管“用户友好”的 ID 是最值得质疑的部分——因为它的存在是为了什么目的。

补充说明:我应该放入 Kimberley Tripp 关于此主题(可能?)最受欢迎的文章的义务链接。http://www.sqlskills.com/BLOGS/KIMBERLY/post/GUIDs-as-PRIMARY-KEYs-andor-the-clustering-key.aspx


我所说的用户友好只是指对于进行查询的用户来说,输入量较少。我意识到我可能只需使用int作为我的主键,但我喜欢GUID作为主键的想法,因为它在未来提供了最大的灵活性。这样做有错吗? - Nate

0

我认为这种做法是不好的设计,但我不知道除此之外是否还有其他问题。请记住,SQLServer会自动将聚集索引分配给主键。在将GUID设置为主键后,您必须将其删除。此外,通常希望将标识列作为主键。因此,按照您所说的做法会让任何没有仔细查看代码的人感到困惑。我建议您将ID列设置为主键、标识列,并在其上放置聚集索引。然后将GUID列设置为唯一键,使其成为非聚集索引并禁止空值。这样做可以实现您想要的效果,同时遵循更多的标准。


4
只有在表设计器中才能自动分配,但在创建表脚本中,您可以完全控制。 - Andrew
2
在您的创建脚本中只需添加 CONSTRAINT PK_table PRIMARY KEY NONCLUSTERED(GuidField),您的主键就不再是聚集索引了。现在添加 CREATE CLUSTERED INDEX ...... 即可完成操作。没有任何麻烦,也不需要删除任何内容。 - marc_s
你可以移除或者不添加聚集索引到主键上,但是它自动为你添加的原因是有道理的。一般来说这是一个好的实践。如果其他人查看代码并尝试使用它,除非他们仔细查看你的代码是如何工作的,否则不会很明显。 - Ben Hoffman
2
仅仅因为SSMS提供了默认值,并不意味着它就是正确的。TLog扩展设置为默认为1兆的文件的10% - 我无法想象在生产系统上我会接受这样的设置。默认值可以帮助保护数据库免受一些可怕的设计缺陷,但它们并不是绝对可靠或总是正确的。 - Andrew
在这种情况下,你会基于什么建立关系? - Nate

0

个人而言,我会选择以下方式:

为您的主键(PK)设置一个内部已知的标识字段(不为最终用户所知,因为他们不可避免地想要以某种方式控制它)。
根据某些业务规则(在应用程序代码中或作为约束强制执行),设置一个用户友好的唯一“ID”。
如果将来需要(例如,如果需要复制),则使用GUID。

现在关于聚集索引,您可能会感到困惑,可以参考MS SQL Server 2000指南


0

你说得对,GUID是很好的对象标识符,在数据库中实现为主键。此外,你也说得对,主键不需要成为聚集索引。

如果GUID是顺序的,那么它们与INT IDENTITY列具有相同的聚集索引特性。SQL Server有一个特定于NewSequentialID的函数,但也有一种通用算法可以创建它们,称为COMB GUID,基于将当前日期时间与随机字节组合在一起的方式,保留了大量的随机性同时保持了顺序性。

需要注意的一件事是,如果你打算在某个时候使用NHibernate,NHibernate本身就知道如何使用COMB GUID策略,甚至可以使用它来进行批量插入,这是无法使用INT IDENTITYNewSequentialID完成的。如果你要使用NHibernate插入多个对象,那么使用COMB GUID策略比其他两种方法更快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接