使用广泛的PK与单独的合成密钥和UQ之间的性能考虑因素是什么?


10

我有几个表,其中的记录可以通过几个广泛的业务领域进行唯一标识。过去,我将这些字段用作PK,并牢记以下好处:

  • 简单; 没有多余的字段,只有一个索引
  • 群集允许快速合并联接和基于范围的过滤器

但是,我听说过创建合成IDENTITY INTPK,而用单独的UNIQUE约束来强制业务密钥的情况。优点是,较窄的PK使得二级索引要小得多。

如果一个表没有比PK其他指标,我看不出有任何理由赞成第二种方法,虽然在一个大表它可能是最好的假设,指数可能在未来是必要的,因此,有利于在狭窄合成PK 。我有什么需要考虑的地方吗?

顺便说一句,我并不是在反对在数据仓库中使用合成密钥,我只是对何时使用单个广泛的PK,何时使用狭窄的PK加广泛的英国感兴趣。


1
您可能会发现网站上的其他问题中有帮助的
杰克说,尽量topanswers.xyz

Answers:


11

使用自然键作为聚簇索引没有明显的缺点

  • 没有非聚集索引
  • 没有引用此表的外键(它是父行)

不利的一面是增加页面拆分,因为数据插入将分布在整个数据中,而不是分布在最后。

在有FK或NC索引的地方,使用狭窄的,数值递增的聚簇索引具有优势。每个NC或FK条目仅重复几个字节的数据,而不是while业务/自然键。

至于原因,请阅读Google的5篇文章

注意我避免使用“主键”。

您可以在代理键上具有聚簇索引,但将PK保留在业务规则中,但不包含在聚簇索引中。只需确保集群是唯一的即可,因为SQL会添加一个“ uniquifier”来实现。

最后,在每个表上都具有一个替代键可能很有意义,但不要盲目:许多表不需要一个,或者在父表中使用复合键就足够了


+1作为参考,特里普夫人在索引中的出色文章。
Fabricio Araujo

2
+1表示性能与主键无关,而与索引无关。
nvogel 2011年

4

尽管我敢说清楚,但如果您需要按事物的ID号查找事物,则在替代键(ID号)上的索引很有用。用户不会处理ID号。他们将处理人类可读的文本。因此,您必须大量传递文本及其ID号,以便用户界面可以显示文本并对ID号进行操作。

如果以这种方式定义dbms,它们将使用这种索引来支持外键。

有时可以通过将ID号用作外键来提高性能,但这不是绝对的改进。在我们的OLTP系统上,在大约130个(我认为)代表性查询的测试套件中,使用自然键的外键优于使用id号的外键。(因为重要信息通常包含在键中,因此使用自然键避免了很多联接。)中位数加速系数是85(使用id编号的联接花费了85倍的时间才能返回行)。

测试表明,在某些表达到数百万行之前,对ID号的联接不会比对我们数据库中的自然键的读取更快。行的宽度与此有关-行越宽意味着页面上可容纳的行越少,因此您必须阅读更多的页面才能获得'n'行。我们几乎所有的表格都以5NF为单位;大多数表都比较狭窄。

到联接开始时,在这里执行简单的读取操作,将关键表和索引放在固态磁盘上可能会将性能提升到几亿行。


3

我有一个完整的oltp数据库,它使用标识列进行聚类+ pk设计。它在插入/搜索时工作得非常快,但我看到了一些问题:
1.索引填充选项没有用,因为插入仅发生在索引的末尾
2.更多的存储空间。我有几千万条记录的表,而1 int本身就占用空间。每个带有pk标识列的表都必须有另一个索引以进行业务搜索,因此需要更多的存储空间。
3.可扩展性。这是最严重的问题。因为每个插入都到达索引的末尾,所以每个插入将仅强调索引的末尾(分配,写操作的io等)。通过使用业务键作为聚类键,您可以将插入片段均匀地分布在索引上。这意味着您只是消除了一个大热点。您可以轻松地将更多文件用于索引,每个文件位于单独的驱动器上,每个驱动器分别工作。

我开始将表从标识列更改为自然键(对于群集和pk可能是分开的)。现在感觉好多了。

我建议以下内容(至少对于oltp数据库而言):
1.以正确的顺序使用正确的列作为聚簇键,以优化最常见的查询
2.将pk正确的列用于您的表

如果聚簇键不简单并且包含chars(char [],varchar,nvarchar),我认为答案是“取决于”,则应分别分析每种情况。

我遵循以下原则:针对最常见的查询进行优化,同时将最坏的情况最小化。

我差点忘了一个例子。我有一些引用自己​​的表。如果该表的主键具有一个标识列,则插入一行可能需要更新,并且即使不是不可能,一次插入多个行也可能很困难(取决于表设计)。


4
您的“热点”概念是一个神话:dba.stackexchange.com/questions/1584/…当您说“现在感觉好多了”时。你基准了吗?
gbn

4
是的,写入是在内存中完成的,而不是直接写入磁盘。如果将20个新行写入页面,则在发生检查点时仅对数据文件进行1次物理写入。
mrdenny

@mrdenny具有足够的插入,将所有内容写入索引的末尾,会将所有io写入请求发送到同一文件。我怀疑使用普通的oltp事务很难重现这种情况,但是使用一些特殊的情况(例如批量/批处理插入记录),使用ssis移动一些业务数据将使您到达那里。
Catalin Adler 2011年

1
@ user973156是,所有请求都将对同一个文件执行,但是直到检查点才真正写入磁盘,检查点仅每分钟发生一次(默认情况下)或写入缓冲区为50%满时。无论如何写入数据,此规则仍然适用。
mrdenny

2
@ user973156使用随机分布的群集密钥将导致索引碎片。索引碎片将导致性能问题。而且您的表将变得足够大,以至于执行索引碎片整理将花费“很长时间”,并吞噬日志空间和潜在的tempDB空间。当我有像金伯利·特里普(Kimberly Tripp)这样的人告诉我这是一个好主意时,我会听。(sqlskills.com/BLOGS/KIMBERLY/post/...
Matt M制作

2

从性能的角度来看,选择哪个键是“主”键完全没有区别。使用PRIMARY KEY和UNIQUE约束来执行您的密钥没有什么区别。

性能由索引的选择和类型以及其他存储选项以及在查询和代码中使用键的方式决定。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.