INT和VARCHAR主键之间是否存在真正的性能差异?
在使用INT和VARCHAR作为MySQL的主键之间是否存在可衡量的性能差异? 我想使用VARCHAR作为引用列表的主键(比如美国,国家代码),同事不会把INT AUTO_INCREMENT作为所有表的主键。
我的论点, 这里详细说明的是,INT和VARCHAR之间的性能差异是可以忽略的,因为每个INT外键引用都需要一个JOIN来表示引用,VARCHAR键将直接显示信息。
那么,有没有人有这个特定用例的经验和与之相关的性能问题?
你提出一个很好的观点,你可以通过使用所谓的自然键而不是代理键来避免一些连接的查询。 只有您可以评估您的应用程序的这种好处是否显着。
也就是说,您可以测量应用程序中最重要的快速查询,因为它们处理大量数据或执行频率非常高。 如果这些查询从消除连接中受益,并且不会受到使用varchar主键的影响,那么就这样做。
不要对数据库中的所有表使用任一策略。 在某些情况下,自然关键可能更好,但在其他情况下,替代关键更好。
其他人提出一个很好的观点,那就是在实践中很less使用自然键来改变或者重复,所以代用键通常是值得的。
这不是关于表演。 这是关于什么是一个很好的主键。 独特和不变的时间。 您可能会认为像国家代码这样的实体不会随着时间的推移而改变,并且会成为主键的合适人选。 但是痛苦的经历很less如此。
INT AUTO_INCREMENT符合“独特且不变的条件”。 因此,偏好。
取决于长度..如果varchar将是20个字符,并且int是4,那么如果使用int,那么您的索引将在磁盘上的索引空间的每页索引空间中具有五倍的节点数量…这意味着遍历该指数将需要五分之一的物理和/或逻辑读数。
所以,如果性能是一个问题,考虑到机会,请始终为表引用一个整数无意义的键(称为代理),对于引用这些表中的行的外键
同时 ,为了保证数据的一致性,每个重要的表还应该有一个有意义的非数字替代键(或唯一索引),以确保重复的行不能被插入(基于有意义的表属性重复)。
对于你正在谈论的特定用途(比如状态查找),这并不重要,因为表的大小非常小。通常,对于less于几千行的表上的索引,性能没有影响。 ..
绝对不。
我已经做了几个…几个… INT,VARCHAR和CHAR之间的性能检查。
一千万logging表(PRIMARY KEY)(唯一的和集群的)具有完全相同的速度和性能(以及子树成本),不pipe我使用的是哪三种。
这就是说…使用任何最适合您的应用程序。 不要担心表演。
对于短代码,可能没有区别。 这是特别真实的,因为持有这些代码的表格可能非常小(最多有几千行),而且不会经常变化(我们最后一次添加新的美国州时)。
对于密钥间差异较大的较大表,这可能是危险的。 例如,考虑使用用户表中的电子邮件地址/用户名。 当有几百万用户时,会发生什么情况,其中一些用户的姓名或电子邮件地址很长。 现在任何时候你需要使用这个键来join这个表,它变得更加昂贵。
至于主键,无论物理上是否有唯一的一行,都应该被确定为主键。
对于引用作为外键,使用自动递增整数作为替代是一个不错的主意,主要有两个原因。
首先,通常join的开销较less。
– 其次,如果需要更新包含唯一的varchar的表,那么更新必须级联到所有的子表,并更新所有的子表以及索引,而使用int代理,只需更新主表和它的索引。
使用替代品的缺点是你可能允许改变替代品的含义:
ex. id value 1 A 2 B 3 C Update 3 to D id value 1 A 2 B 3 D Update 2 to C id value 1 A 2 C 3 D Update 3 to B id value 1 A 2 C 3 B
这一切都取决于你真正需要在你的结构中担心什么,最重要的是什么。
在HauteLook中,我们改变了许多表格来使用自然键。 我们确实体验到真实世界的性能提升。 正如你所提到的,我们现在的许多查询使用的联接less,这使查询更高性能。 如果有意义,我们甚至会使用复合主键。 也就是说,如果有代理键,一些表格更容易处理。
而且,如果您让人们将接口写入您的数据库,代理键可能会有所帮助。 第三方可以依赖于代理密钥只在非常罕见的情况下才会改变的事实。
代用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) );
为什么?
- InnoDB二级密钥需要额外的查找; 通过将这一对移动到PK,这是避免了一个方向。
- 二级索引是“覆盖”的,所以不需要额外的查找。
- 这张表是因为摆脱了
id
和一个索引而变小了。
另一个案例( 国家 ):
country_id INT ... -- versus country_code CHAR(2) CHARACTER SET ascii
新手经常将country_code标准化为4字节的INT
而不是使用“自然的”2字节,几乎不变的2字节string。 更快,更小,联接更less,可读性更强。
这篇文章是关于Oracle,但它可能适用。
我面临同样的困境。 我做了一个DW(星座模式),有3个事实表,道路事故,事故车辆和意外事故伤亡人员。 数据包括1979年至2012年在英国logging的所有事故以及60个维度表。 总共有大约2000万条logging。
事实表关系:
+----------+ +---------+ | Accident |>--------<| Vehicle | +-----v----+ 1 * +----v----+ 1| |1 | +----------+ | +---<| Casualty |>---+ * +----------+ *
RDMS:MySQL 5.6
事故索引本身就是一个varchar(数字和字母),有15个数字。 一旦事故指标永远不会改变,我试图不用代理钥匙。 在i7(8核)计算机中,DW变得太慢以至于在根据维度的1200万个logging的负载之后查询。 经过大量的重新工作和joinbigint代理键我得到了平均20%的速度性能提升。 然而,低绩效的收益,但有效的尝试。 我在MySQL调优和集群工作。
不确定性能的影响,但似乎一个可能的妥协,至less在开发过程中,将包括自动递增的整数“代理”键以及您的意图的,独特的“自然”键。 这将使您有机会评估性能,以及其他可能的问题,包括自然键的可变性。
问题是关于MySQL,所以我说有一个显着的差异。 如果是关于Oracle(将数字存储为string – 是的,我不相信它),那么没有太大的区别。
在表中的存储不是问题,但更新和参考索引是。 涉及查询基于主键的logging的查询很频繁 – 您希望他们尽可能快地发生,因为他们经常发生。
事情是一个CPU自然处理4字节和8字节的整数,在硅 。 它比较两个整数是非常快的 – 它发生在一个或两个时钟周期。
现在看一个string – 它由许多字符组成(每个字符多于一个字节)。 比较两个string的优先级不能在一个或两个周期内完成。 相反,string的字符必须迭代,直到find差异。 我敢肯定,在某些数据库中有一些技巧可以使其更快,但这并不重要,因为int比较是自然而然地由CPU完成的。
我的一般规则 – 每个主键都应该是一个自动增加的INT,尤其是在使用ORM(Hibernate,Datanucleus等)的OO应用程序中,在对象之间有很多关系的时候,它们通常总是作为一个简单的FK来实现,数据库解决这些问题对于您的应用程序的响应速度非常重要。
像往常一样,没有一揽子答案。 '这取决于!' 而我并不是一个开玩笑的人。 我对原始问题的理解是对小型表格的键 – 例如Country(整数id或char / varchar代码)是一个潜在巨大的表,如地址/联系人表的外键。
当你想从数据库返回数据时,有两种情况。 首先是一个列表/searchtypes的查询,你想列出所有的联系与州和国家的代码或名称(id不会帮助,因此将需要查找)。 另一个是主键上的getscheme,其显示了单个联系人logging,其中需要显示国家,国家的名称。
对于后者而言,FK的基础可能并不重要,因为我们将单个logging或几个logging的表格和关键读取结合在一起。 前者(search或列表)的情况可能会受到我们的select的影响。 由于它需要显示国家(至less一个可识别的代码,甚至可能search本身包括一个国家代码),而不必通过代理键join另一个表可能(我只是在这里谨慎,因为我没有实际testing这个,但似乎很有可能)提高性能; 尽pipe它确实有助于search。
由于代码规模较小,通常对于国家和州来说不超过3个字符,所以在这种情况下使用自然键作为外键是可以的。
另一种情况,其中键依赖于较长的varchar值,也许在较大的表上; 代理键可能有优势。