代理与自然关键:性能差异的硬数字?

时间:2009-08-04 18:36:34

标签: database database-design primary-key key database-performance

在代理人和自然键之间存在一场健康的辩论:

SO Post 1

SO Post 2

我认为,这似乎与大多数人(这是一个微不足道的多数)一致,是你应该使用代理键,除非自然键是完全明显的并且保证不会改变。然后你应该对自然键强制执行唯一性。这几乎意味着代理键几乎所有的时间。

两种方法的示例,从公司表开始:

1:代理键:表具有ID字段,即PK(和标识)。公司名称必须按州独特,因此存在唯一约束。

2:自然键:表使用CompanyName和State作为PK - 同时满足PK和唯一性。

假设公司PK用于其他10个表格。我的假设,没有数字可以支持,这就是代理关键方法在这里要快得多。

我见过的关于自然键的唯一有说服力的论据是使用两个外键作为自然键的多对多表。我认为在这种情况下它是有道理的。但如果你需要重构,你可能会遇到麻烦;我认为这超出了这篇文章的范围。

是否有人在一组使用代理键 vs。的表中使用性能差异的文章进行了比较自然键?环顾SO和谷歌并没有产生任何有价值的东西,只是大量的理论研究。


重要更新:我已经开始构建一个测试表来回答这个问题。它看起来像这样:

  • PartNatural - 使用的零件表 唯一的PartNumber作为PK
  • PartSurrogate - 部分表 使用ID(int,identity)作为PK和 在PartNumber上有唯一索引
  • Plant - ID(int,identity)as PK
  • 工程师 - 身份证(int,身份)为PK

每个零件都与工厂相连,工厂中零件的每个实例都与工程师联系在一起。如果有人对这个测试床有问题,现在是时候了。

2 个答案:

答案 0 :(得分:9)

同时使用!自然键可以防止数据库损坏(不一致可能是一个更好的词)。当“正确”自然键(以消除重复行)由于长度或所涉及的列数而表现不佳时,出于性能目的,还可以添加代理键以用作其他表中的外键而不是自然密钥...但是自然密钥应该作为备用密钥或唯一索引保留,以防止数据损坏和提高数据库的一致性......

很多人(在这个问题上的“辩论”中),可能是由于什么是假设 - 你必须使用 主键 用于其他表中的连接和外键。这是假的。您可以使用任何作为其他表中外键的目标。它可以是主键,备用键或任何唯一索引或唯一约束。对于连接,你可以使用任何东西来连接条件,它甚至不必是一个键,或一个idex,甚至独特! (虽然如果它不是唯一的,你会在它创建的笛卡尔积中得到多行。

答案 1 :(得分:3)

自然键与值中的代理键不同,不是类型。

任何类型都可以用于代理键,例如VARCHAR用于系统生成的slug或其他类型。

但是,代理键的大多数使用类型都是INTEGERRAW(16)(或RDBMS用于GUID的任何类型),

比较代理整数和自然整数(如SSN)需要完全相同的时间。

比较VARCHAR s考虑整理,它们通常比整数长,这会降低它们的效率。

比较一组INTEGER可能也不如比较一个INTEGER

在数据类型较小的情况下,这种差异可能是百分比百分比获取页面,遍历索引,获取数据库锁存器等所需的时间。

以下是数字(MySQL):

CREATE TABLE aint (id INT NOT NULL PRIMARY KEY, value VARCHAR(100));
CREATE TABLE adouble (id1 INT NOT NULL, id2 INT NOT NULL, value VARCHAR(100), PRIMARY KEY (id1, id2));
CREATE TABLE bint (id INT NOT NULL PRIMARY KEY, aid INT NOT NULL);
CREATE TABLE bdouble (id INT NOT NULL PRIMARY KEY, aid1 INT NOT NULL, aid2 INT NOT NULL);

INSERT
INTO    aint
SELECT  id, RPAD('', FLOOR(RAND(20090804) * 100), '*')
FROM    t_source;

INSERT
INTO    bint
SELECT  id, id
FROM    aint;

INSERT
INTO    adouble
SELECT  id, id, value
FROM    aint;

INSERT
INTO    bdouble
SELECT  id, id, id
FROM    aint;

SELECT  SUM(LENGTH(value))
FROM    bint b
JOIN    aint a
ON      a.id = b.aid;

SELECT  SUM(LENGTH(value))
FROM    bdouble b
JOIN    adouble a
ON      (a.id1, a.id2) = (b.aid1, b.aid2);

t_source只是一个包含1,000,000行的虚拟表。

aintadoublebintbdouble包含完全相同的数据,但aint的整数为PRIMARY KEYadouble有一对两个相同的整数。

在我的机器上,两个查询都运行14.5秒,+ / - 0.1秒

性能差异(如果有)在波动范围内。