在MySQL中使用INT和VARCHAR作为主键之间有可测量的性能差异吗?我想使用VARCHAR作为参考列表的主键(认为美国州,国家代码)和同事不会在INT AUTO_INCREMENT作为所有表的主键上让步。

我的论点是,INT和VARCHAR之间的性能差异可以忽略不计,因为每个INT外键引用都需要一个JOIN来理解引用,VARCHAR键将直接显示信息。

那么,有人对这个特殊的用例以及与之相关的性能问题有过经验吗?


当前回答

取决于长度..如果varchar是20个字符,而int是4,那么如果你使用int类型,你的索引在磁盘上每页索引空间的节点数将是原来的5倍……这意味着遍历索引将需要五分之一的物理和/或逻辑读取。

因此,如果性能是一个问题,如果有机会,总是为您的表使用一个整体的无意义键(称为代理),对于引用这些表中的行的外键……

同时,为了保证数据一致性,每个重要的表也应该有一个有意义的非数字替代键(或唯一索引),以确保不能插入重复的行(基于有意义的表属性的重复)。

对于您所谈论的特定用途(如状态查找),这真的无关紧要,因为表的大小是如此之小。一般来说,在小于几千行的表上建立索引对性能没有影响……

其他回答

请允许我说,考虑到性能范围(开箱即用定义),肯定有区别:

1-在应用程序中使用代理int更快,因为你不需要在你的代码或查询中使用ToUpper(), ToLower(), ToUpperInvarient()或ToLowerInvarient(),这4个函数有不同的性能基准。请参阅关于此的Microsoft性能规则。(申请的表现)

2-使用代理int保证不随时间改变键。甚至国家代码也可能发生变化,请参阅维基百科ISO代码如何随时间变化。这将花费大量时间来更改子树的主键。(数据维护的表现)

3- ORM解决方案似乎有问题,比如当PK/FK不是int时NHibernate。开发人员(性能)

对于短代码,可能没有区别。当保存这些代码的表可能非常小(最多几千行)并且不经常更改(我们上一次添加新的US State是什么时候)时,这一点尤其正确。

对于键之间变化较大的大型表,这可能是危险的。例如,考虑使用user表中的电子邮件地址/用户名。如果你有几百万用户,其中一些用户有很长的名字或电子邮件地址,会发生什么?现在,任何时候你需要使用这个键来连接这个表,它就变得非常昂贵。

取决于长度..如果varchar是20个字符,而int是4,那么如果你使用int类型,你的索引在磁盘上每页索引空间的节点数将是原来的5倍……这意味着遍历索引将需要五分之一的物理和/或逻辑读取。

因此,如果性能是一个问题,如果有机会,总是为您的表使用一个整体的无意义键(称为代理),对于引用这些表中的行的外键……

同时,为了保证数据一致性,每个重要的表也应该有一个有意义的非数字替代键(或唯一索引),以确保不能插入重复的行(基于有意义的表属性的重复)。

对于您所谈论的特定用途(如状态查找),这真的无关紧要,因为表的大小是如此之小。一般来说,在小于几千行的表上建立索引对性能没有影响……

和往常一样,没有统一的答案。“这取决于!我不是在开玩笑。我对原始问题的理解是小表上的键-像Country(整数id或char/varchar代码)是潜在的大表(如地址/联系表)的外键。

当您希望从DB返回数据时,这里有两种场景。首先是一个列表/搜索类型的查询,其中您希望列出所有带有州和国家代码或名称的联系人(id没有帮助,因此需要查找)。另一个是在主键上的get场景,它显示单个联系人记录,其中需要显示州名和国家。

For the latter get, it probably does not matter what the FK is based on since we are bringing together tables for a single record or a few records and on key reads. The former (search or list) scenario may be impacted by our choice. Since it is required to show country (at least a recognizable code and perhaps even the search itself includes a country code), not having to join another table through a surrogate key can potentially (I am just being cautious here because I have not actually tested this, but seems highly probable) improve performance; notwithstanding the fact that it certainly helps with the search.

由于代码很小——国家和州通常不超过3个字符,在这种情况下使用自然键作为外键是可以的。

另一种情况是,键依赖于较长的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,更可读。