在MySQL中使用INT和VARCHAR作为主键之间有可测量的性能差异吗?我想使用VARCHAR作为参考列表的主键(认为美国州,国家代码)和同事不会在INT AUTO_INCREMENT作为所有表的主键上让步。
我的论点是,INT和VARCHAR之间的性能差异可以忽略不计,因为每个INT外键引用都需要一个JOIN来理解引用,VARCHAR键将直接显示信息。
那么,有人对这个特殊的用例以及与之相关的性能问题有过经验吗?
在MySQL中使用INT和VARCHAR作为主键之间有可测量的性能差异吗?我想使用VARCHAR作为参考列表的主键(认为美国州,国家代码)和同事不会在INT AUTO_INCREMENT作为所有表的主键上让步。
我的论点是,INT和VARCHAR之间的性能差异可以忽略不计,因为每个INT外键引用都需要一个JOIN来理解引用,VARCHAR键将直接显示信息。
那么,有人对这个特殊的用例以及与之相关的性能问题有过经验吗?
当前回答
代理AUTO_INCREMENT有害的常见情况:
常见的模式模式是多对多映射:
CREATE TABLE map (
id ... AUTO_INCREMENT,
foo_id ...,
bar_id ...,
PRIMARY KEY(id),
UNIQUE(foo_id, bar_id),
INDEX(bar_id) );
这种模式的性能要好得多,特别是在使用InnoDB时:
CREATE TABLE map (
# No surrogate
foo_id ...,
bar_id ...,
PRIMARY KEY(foo_id, bar_id),
INDEX (bar_id, foo_id) );
Why?
InnoDB二级键需要额外的查找;通过将配对移动到PK中,这在一个方向上是避免的。 二级索引是“覆盖”的,因此不需要额外的查找。 这个表变小了,因为去掉了id和一个索引。
另一个案例(国家):
country_id INT ...
-- versus
country_code CHAR(2) CHARACTER SET ascii
新手经常将country_code规范化为4字节INT,而不是使用“自然的”2字节,几乎不变的2字节字符串。更快、更小、更少的join,更可读。
其他回答
对于短代码,可能没有区别。当保存这些代码的表可能非常小(最多几千行)并且不经常更改(我们上一次添加新的US State是什么时候)时,这一点尤其正确。
对于键之间变化较大的大型表,这可能是危险的。例如,考虑使用user表中的电子邮件地址/用户名。如果你有几百万用户,其中一些用户有很长的名字或电子邮件地址,会发生什么?现在,任何时候你需要使用这个键来连接这个表,它就变得非常昂贵。
您提出了一个很好的观点,即可以通过使用所谓的自然键而不是代理键来避免一些连接查询。只有您才能评估这样做的好处在您的应用程序中是否显著。
也就是说,您可以测量应用程序中对快速最重要的查询,因为它们处理大量数据或执行非常频繁。如果这些查询可以从消除连接中获益,并且不会因使用varchar主键而遭受损失,那么就这样做。
不要对数据库中的所有表使用这两种策略。在某些情况下,天然键可能更好,但在其他情况下,替代键可能更好。
另一些人提出了一个很好的观点,即在实践中,自然键很少永远不会更改或有重复,因此代理键通常是值得的。
这与性能无关。这是关于什么是一个好的主键。独一无二且随时间不变。您可能认为国家代码之类的实体永远不会随着时间而改变,并且是主键的良好候选者。但痛苦的经验是,这种情况很少发生。
INT AUTO_INCREMENT满足“唯一且随时间不变”的条件。因此才会有偏好。
在HauteLook,我们将许多表改为使用自然键。我们确实体验到了真实世界的性能提升。正如您所提到的,我们的许多查询现在使用更少的连接,这使得查询的性能更高。如果有意义,我们甚至会使用复合主键。话虽如此,有些表如果有代理键就更容易使用。
另外,如果您让人们编写到您的数据库的接口,代理键可能会很有帮助。第三方可以依赖代理键只在非常罕见的情况下才会更改这一事实。
绝对不是。
我做过几次……几个……INT, VARCHAR和CHAR之间的性能检查。
有一个PRIMARY KEY(唯一的和聚集的)的1000万条记录表,无论我使用哪一个都具有完全相同的速度和性能(以及子树成本)。
话虽如此……使用最适合您的应用程序的任何东西。不要担心性能。