主要问题

时间:2010-07-28 20:56:45

标签: database database-design

使用单列主键与复合主键有什么好处吗?

我有一个由两个id列组成的表,它们组成了主键。

这有什么不利之处吗?有没有令人信服的理由让我投入一个独一无二的专栏?

12 个答案:

答案 0 :(得分:9)

数据库规范化坚果会告诉你一件事。

我将对多年来学到的东西提出自己的看法。我将自动增量ID字段添加到每个($&(@#$)#我的一张桌子中。从长远来看,一百万次更容易单挑而不受惩罚。

这是来自“战壕中”的开发者。

答案 1 :(得分:3)

单列键易于编写,易于维护且易于理解。

如果你有大量的行 - 数十亿? - 也许在这里保存一个字节,这将有所帮助。

但如果你不是在考虑极端情况,那么优化“简单”通常是最好的方法。

答案 2 :(得分:3)

如果你是一名程序员,数据库对你来说只不过是一个美化的物品商店,那么肯定无论如何都会注入代理密钥。事实上,更好一点,只需将所有数据库架构设计和数据库交互委托给您喜欢的ORM,然后完成它。实际上,当我想要一个小型或中型的物品商店时,这正是我所做的。

如果您正在接近信息系统或信息管理问题,那么这是一个完全不同的故事。当你开始处理从多个来源集成的数百万个(或更可能是100个)数百个脏记录时,其中几个或所有记录都不在你的控制之下;在这一点上,对“身份”问题的简单回答诱人的诱惑是一个陷阱。

是的,您有时仍会在内部引入代理键,以便在覆盖索引时提供简洁的FK关系并提高缓存效率;但是,你在管理自然关键/代理 - 关键关系时付出了巨大的痛苦,从而获得了这些好处。

在这种情况下,确保不允许代理键泄漏非常重要。业务逻辑层的公共API应该使用自然密钥,文档/记录缓存之上的任何内容都不应该知道代理密钥的存在。请注意,针对现有代理键匹配更新的成本可能过高,而且可扩展性远远高于通过内部网络为每个请求移动一些额外字节的增量成本。

总之:

  1. 如果DB只是用作对象存储:让ORM担心对象标识,几乎肯定会有代理键。

  2. 如果数据库被用作数据库:代理密钥的引入是工程设计决策,在两个方向上都有严重的权衡。该决定需要根据具体情况进行,并充分认可所产生的成本,以换取任何方式获得的利益。

  3. <强>更新

    代理钥匙的“便利性”实际上只是能够解决身份问题。这在数据库中通常是必需的,并且在我允许的情况下在缓存层中是合理的,但除此之外它会导致脆弱的数据设计。问题是身份不是具有一个正确的答案的东西。对于非平凡的数据密集型系统,您通常会发现自己需要使用等价类,而不是参考标识,面向对象编程会使我们认为正常

    真正归结为认识到“主键”的整个概念是为帮助关系模型有效工作而发明的虚构;但是,采用代理键,巩固了这种虚构,使整个系统变得脆弱和不灵活。业务逻辑需要能够提供自己的相等定义 - 有时候,同一个文件的四个副本需要被视为四个文件,有时候它们应该被视为无法区分原始文件;当你编辑其中一个时,那是一个新的文件吗?相同的文件?这两个问题的答案当然是是,当...... 使用自然键提供了在概念等价类方面工作的关键能力。如果让代理键感染您的业务逻辑,您很快就会失去这一点。

答案 3 :(得分:2)

过去我不得不使用多列主键,很快就变成了一场噩梦。

如果您有一个引用第一个表的表,它是如何包含该主键的?现在添加另一个仅引用第二个表但需要在第一个表中查找数据的表。现在另一个......在兔子洞的下方。

如果你知道你只有一个表,那么可能没有任何问题 - 使用哪个更好地代表你的数据。但是如果你在连接中使用它,你可能会很快失去性能。

答案 4 :(得分:2)

  

使用单列主键与复合[sic]主键有什么好处?

是。如果主键也恰好是聚簇索引,则通常会为表中的每个辅助索引完全复制聚簇索引。因此,具有更复杂的聚簇索引(这是复合材料所能得到的)意味着存储成本的增加。此外,对表的外部引用需要指定两个字段以引用唯一条目,这意味着进一步的存储成本。开发时间的成本可能会更高,因为连接的复杂性略有增加。

另一方面,根据两个关键字段的值的分布情况,可能会大大改善对表的并发访问,因为按时间顺序连续插入可能出现在不同的物理页面上;例如,如果您的字段与时间无关(并且非单调,如自动增量),例如 clientID ,或类似的情况,则可能出现这种情况。这对于高并发环境中的性能可能很重要。

  

我有一个由两个id列组成的表,它们组成了主键。

     

这有什么不利之处吗?对我来说是否有令人信服的理由?   投入一个独一无二的第三列?

如果查询表的最常见方式是将这三个字段指定为限制,那么在复合键中包含所有三个字段可能是最快的查找。

还有另外一点我差点忘了。由于具有复合键意味着从其他表中对表的外部引用必须指定键中的所有字段,这也意味着在其他表上执行的某些查询需要对表的复合索引的一个或多个部分的限制可以在不需要连接的情况下执行。为了性能,这可以被认为类似于非规范化的概念(并且可以说牺牲了一点易维护性)。

答案 5 :(得分:1)

一般来说,我更喜欢有一个代理键,因为很少有真正好的自然键(关键问题不是唯一性,而是随着时间的推移而变化),自然键越长,它在用作PK。如果您有自然键,则应在其上创建唯一索引,然后使用代理键作为用于连接到其他表的PK。这强制了自然关键数据的唯一性,但修复了连接性能的问题以及自然键更改时更新所有子记录的额外时间。

有一种情况我忽略了这个,这是一个连接表。如果它是一个用于强制执行多对多关系的表,并且只包含来自其他表的两个代理键,那么添加代理键实际上没有任何好处。通常,各个键用于连接而不是PK和代理键几乎不会改变。在一个连接表中,我只添加了我需要的两个colmns而没有别的。

答案 6 :(得分:0)

在我所知道的大多数数据库(MySQL,PostgreSQL)中,复合键将生成一个索引。因此,如果您将密钥指定为复合,则DB应该为您提供使用该密钥从DB查找元组的有效方法。我认为所有数据库都是如此。我认为你不必为那里的表现而烦恼。

答案 7 :(得分:0)

请勿使用多列密钥。它们很难维护,特别是如果密钥的组件不是人类可理解的。

改为使用内部生成的密钥。

答案 8 :(得分:0)

假设你有一个复合主键(例如field1和field2)而不是一个自动增量标识符。客户端的要求是非常多变的,经过一些开发后,客户端说field2不是必须的,它可以为空,它不可能继续作为表的主键。想象一下,这张表是您模型中最重要的表之一。如果字段2不在复合主键中,则应更改所有外键。这是改变整个模型的主键的噩梦。

如果有很多外键,我认为为每个表添加几个键只是为了建立链接并不是一个好主意。

答案 9 :(得分:0)

我不确定是否有足够的信息让我们为您打电话。以下是一些可能有用的观察结果。

是聚簇索引的主键吗?是否通过外键引用其他表的表?如果是,那么您可以从单列密钥中受益,因为该密钥将出现在其他表中。这就是节省空间的方法。

如果该表未被其他表引用,那么您将在表中使用额外的空间而没有太多额外的好处。而且,如果此表现在只包含两列,那么您将把表大小增加50%。

如果您为主键使用额外的列,请不要忘记您的自然键(双列键)。在组合键上创建唯一约束。您仍希望保持实际数据的完整性。

答案 10 :(得分:0)

决定应始终基于要求和数据的预期含义。仅具有单个属性键的表明确强制执行不同类型的约束,并暗示您的表与具有多属性键的同一表具有非常不同的含义。另一方面,如果您实际上不需要在任何地方使用它,那么添加额外的唯一列也会浪费资源并增加无意义的复杂性。

答案 11 :(得分:0)

对自动递增列的一个警告是它可以给出对唯一性的错误印象。当然,您的标识列始终是唯一的,但这只是您附加到表中的无意义值。除非您还有一组唯一约束附加到表示表的实际语义主键的列集,否则您无法保证有意义的唯一性。