我有几个表,其中的记录可以用几个广泛的业务领域进行唯一标识。过去,我将这些字段用作 PK,考虑到这些好处:
- 简单; 没有无关的字段,只有一个索引
- 集群允许快速合并连接和基于范围的过滤器
但是,我听说过创建合成IDENTITY INT
PK 的案例,而不是使用单独的UNIQUE
约束来强制执行业务密钥。优点是窄 PK 使得二级索引小得多。
如果一个表除了 PK 之外没有其他索引,我看不出有任何理由支持第二种方法,尽管在一个大表中最好假设将来可能需要索引,因此支持窄合成 PK . 我是否遗漏了任何注意事项?
顺便说一下,我并不是反对在数据仓库中使用合成密钥,我只是对何时使用单个宽泛 PK 以及何时使用窄 PK 加上宽泛 UK 感兴趣。
使用自然键作为聚簇索引没有明显的缺点
缺点是会增加页面拆分,因为数据插入将分布在整个数据中,而不是在末尾。
如果您确实有 FK 或 NC 索引,则使用窄的、数字的、递增的聚集索引具有优势。您只需为每个 NC 或 FK 条目重复几个字节的数据,而不是 while 业务/自然键。
至于为什么,请阅读谷歌的 5 篇文章
注意我避免使用“主键”。
您可以在代理键上使用聚集索引,但将 PK 保留在业务规则上,但作为非聚集索引。只要确保集群是唯一的,因为 SQL 会添加一个“uniquifier”来实现它。
最后,在每个表上都有一个代理键但不是盲目地使用代理键可能是有意义的:许多表不需要代理键,或者父表中的复合键就足够了
尽管我冒着风险陈述显而易见的事实,但如果您需要通过 ID 号定位事物,则代理键(ID 号)上的索引很有用。用户不会去处理id号;他们将处理人类可读的文本。因此,您必须多次传递文本及其 ID 号,以便用户界面可以显示文本并对 ID 号进行操作。
dbms 将使用那种索引来支持外键,如果你这样定义的话。
有时可以通过使用 ID 号作为外键来提高性能,但这不是绝对的提高。在我们的 OLTP 系统上,在大约 130 个(我认为)代表性查询的测试套件中,使用自然键的外键优于使用 ID 号的外键。(因为重要信息通常包含在键中,所以使用自然键避免了很多连接。)中值加速是 85 倍(使用 id 号的连接花费 85 倍的时间返回行)。
测试表明,在某些表达到数百万行之前,对 ID 号的连接不会比对我们数据库中自然键的读取执行得更快。行的宽度与此有很大关系——较宽的行意味着页面上适合的行数较少,因此您必须阅读更多页面才能获得“n”行。几乎我们所有的表都是 5NF;大多数桌子都很窄。
当连接开始执行简单读取时,将关键表和索引放在固态磁盘上可能会使性能达到数亿行。
我有一个完整的 oltp 数据库,设计使用标识列进行聚类 + pk。它在插入/查找时工作得非常快,但我看到了一些问题:
1. 索引填充选项无用,因为插入仅发生在索引的末尾
2. 更多存储空间。我有几千万条记录的表,1个int本身就占空间。每个带有 pk 标识列的表都必须有另一个用于业务搜索的索引,因此需要更多的存储空间。
3.可扩展性。这是最糟糕的问题。因为每次插入都会到达索引的末尾,所以每次插入只会强调索引的末尾(分配、用于写入的 io 等)。通过使用业务键作为集群键,您可以在索引上均匀分布插入。这意味着您刚刚消除了一个大热点。您可以轻松地使用更多文件作为索引,每个文件都在一个单独的驱动器上,每个驱动器单独工作。
我开始将我的表从标识列更改为自然键(对于聚类和 pk 可能是分开的)。现在感觉好多了。
我会建议以下内容(至少对于 oltp 数据库):
1. 以正确的顺序将正确的列用作聚类键,以优化最频繁的查询
2. 使用 pk 对您的表有意义的正确列
如果聚簇键不简单并且包含字符(char[]、varchar、nvarchar),我认为答案是“视情况而定”,您应该单独分析每种情况。
我坚持以下原则:优化最常见的查询,同时尽量减少最坏的情况。
我差点忘了一个例子。我有一些引用自己的表。如果该表的主键有标识列,则插入一行可能需要更新,并且一次插入多行即使不是不可能也可能很困难(这取决于表设计)。
从性能的角度来看,选择哪个键是“主”键根本没有区别。使用 PRIMARY KEY 和 UNIQUE 约束来强制执行密钥之间没有区别。
性能取决于索引和其他存储选项的选择和类型,以及键在查询和代码中的使用方式。