12

考虑这个示例表(假设 SQL Server 2005):

create table product_bill_of_materials
(
    parent_product_id int not null,
    child_product_id int not null,
    quantity int not null
)

我正在考虑包含两个 product_id 列(我肯定想要一个唯一约束)的复合主键,而不是一个单独的唯一 ID 列。问题是,从性能的角度来看,该主键是否应该集群?

我是否还应该在每个 ID 列上创建一个索引,以便更快地查找外键?我相信这张表在读比写上会受到更多的打击。

4

5 回答 5

16

正如其他几个人已经说过的那样,这取决于您将如何访问该表。但请记住,只要该列首先出现,任何 RDBMS 都应该能够使用聚集索引按单个列进行搜索。例如,如果您的聚集索引在 (parent_id, child_id) 上,则您不需要在 (parent_id) 上建立另一个单独的索引。

您最好的选择可能是 (parent_id, child_id) 上的聚集索引,它也恰好是主键,在 (child_id) 上具有单独的非聚集索引。

最终,应该在您了解如何访问数据库之后解决索引问题。如果可以的话,提出一些标准的性能压力测试,然后使用分析工具(SQL Profiler for SQL Server)分析行为并从那里进行性能调整。如果您不具备提前执行此操作的专业知识或知识,请尝试(希望是有限的)应用程序发布,收集性能指标,并查看您需要在哪里提高性能并找出哪些索引会有所帮助.

如果你做对了,你应该能够捕获如何访问数据库的“典型”配置文件,然后当你尝试各种索引方法时,你可以在测试服务器上一遍又一遍地重新运行它。

在您的情况下,我可能只是在 (parent_id, child_id) 上放置一个聚集 PK,然后仅当我看到可以帮助的性能问题时才添加非聚集索引。

于 2008-12-23T17:35:08.740 回答
6

“您最常查询的内容”不一定是选择集群索引的最佳理由。最重要的是您查询什么以获得多行。集群是一种适合于高效地以最少的磁盘读取次数获得多行的策略。

最好的例子是客户的销售历史。

假设您在 Sales 表上有两个索引,一个在 Customer 上(可能还有日期,但这一点适用于任何一种方式)。如果您最常在 CustomerID 上查询该表,那么您会希望将所有客户的销售记录放在一起,以便为所有记录提供一到两次磁盘读取。

主键 OTOH 可能是代理键或 SalesId,但在任何情况下都是唯一值。如果这是聚集的,与普通的唯一索引相比,它没有任何好处。

编辑:让我们来讨论这个特定的表格 - 它会揭示更多的微妙之处。

“自然”主键很可能是 parentid + childid。但按什么顺序?Parentid + childid 并不比 childid + parentid 更独特。出于聚类目的,哪种排序更合适?有人会假设它必须是 parentid + childid,因为我们会想问:“对于给定的项目,它的成分是什么”?但是,不是不太可能想要走另一条路,并询问“对于给定的成分,它是哪些项目的组成部分?”。

加入“覆盖索引”的考虑,在索引中包含满足查询所需的所有信息。如果这是真的,那么您永远不需要阅读记录的其余部分;所以集群是没有好处的;只需阅读索引就足够了。(顺便说一句,这意味着同一对字段上的两个索引,顺序相反;在这种情况下,这可能是正确的做法。或者至少一个上的复合索引,另一个上的单字段索引。 )

但这仍然不能决定哪个应该集群。这最终可能取决于哪些查询实际上需要获取 Quantity 字段的记录。

即使是这样一个清晰的例子,原则上最好在您可以用真实数据测试它们之前(显然是在生产之前)对其他索引做出决定;但是在这里问猜测是没有意义的。测试总是会给你正确的答案。

忘记担心会减慢插入速度,直到遇到问题(在大多数情况下永远不会发生),并且可以测试以确保放弃有用的索引以获得可衡量的好处。

但是,事情仍然不确定,因为像这样的联结表也经常涉及许多其他类型的查询。因此,我只需要选择一个并根据需要进行测试,因为应用程序凝胶化,并且用于测试的数据量变得可用。

顺便说一句,我希望它最终会在 parentid + childid 上进行 PK;childid 的非唯一索引;和第一个集群。如果您更喜欢代理 PK,那么您仍然需要 parentid + childid 上的唯一索引,聚集。对代理键进行聚类不太可能是最优的。

于 2008-12-23T16:48:04.387 回答
2

这里真正的问题是您最想查询什么?如果您一直在寻找这两个值,那么集群应该在这对上。如果您要更多地查询其中一个或另一个,您会希望集群在那个特定的那个上。

于 2008-12-23T16:32:40.470 回答
0

我想对你最后的陈述归零。“我相信这张表在读取方面会比在写入方面受到更多影响。” 如果是这种情况,那么您可能想要重索引。我们不在每件事上都使用大量索引的原因是您为表的更新和插入付出了性能损失。当我们的表提供更多的读取而不是写入时,就要为索引付出代价。

至于要聚类什么,您应该考虑如何最好地使用该表。如果您的表受到大量范围查询(WHERE col1 IS BETWEEN a AND b)的影响,则对表进行集群,以便范围查询已经在磁盘上按顺序设置。在 SQL Server 中,有时我们通过 PK 免费获得集群,而我们忘记了从什么开始最好集群。

至于表上的 FK 约束,因为您说读多于写,这可能是可以接受的。如果这是一个包含大量插入的表,则每个 FK 约束都需要针对父表进行验证,这可能无法为您提供所需的性能。

好问题。

于 2008-12-23T16:41:30.377 回答
-1

既然您说“我正在考虑使用复合主键”,那么您可能还有时间改变主意。我使用了许多复合键,并且我一直在寻找希望我没有使用的理由。也许其他人会不同意我的观点。

我同意 Mitchel 的回答,无论您最常查询什么,集群都会继续进行。

于 2008-12-23T16:40:04.840 回答