对所有基于文本的字段使用通用varchar(255)是否有缺点?


100

我有一个contacts包含字段,如表postcodefirst namelast nametowncountryphone number等等,所有这些都被定义为VARCHAR(255)即使没有这些领域都不会接近有255个字符。(如果您想知道,是因为Ruby on Rails迁移VARCHAR(255)默认将String字段映射到String字段,而我从不费心去重写它)。

由于VARCHAR将仅存储字段的实际字符数(以及字段长度),因此,使用VARCHAR(16)over 相比有什么明显的优势(性能还是其他方面)VARCHAR(255)

此外,这些字段中的大多数都有索引。字段上较大的VARCHAR大小是否会完全影响索引的大小或性能?

仅供参考,我正在使用MySQL 5。


2
@ceejayoz,指出接受的答案是错误的,但没有解释为什么并没有真正帮助。更糟糕的是,接受的答案会随着时间而变化,您的评论会使人们误以为新的接受的答案是不正确的。
吉利

1
@Gili删除了我的评论,因为OP显然改变了他们的接受度。好点,以后我会指出我正在谈论的答案以及原因。
ceejayoz

在此重复问题的一些其他的答案,stackoverflow.com/questions/1262174/...
詹姆斯麦克马洪

Answers:


129

在存储中,VARCHAR(255)它足够聪明,可以只存储给定行上所需的长度,而CHAR(255)后者通常会存储255个字符。

但是,由于您使用MySQL标记了这个问题,因此我将提到一个MySQL特定的技巧:当将行从存储引擎层复制到SQL层时,VARCHAR将转换字段CHAR以获得利用固定宽度行的优势。因此,内存中的字符串将填充到声明的VARCHAR的最大长度

当查询隐式生成临时表时(例如在排序或时)GROUP BY,这会占用大量内存。如果您使用很多VARCHAR(255)字段来存储不需要那么长的数据,这会使临时表变得非常大。

您可能还想知道,这种“填充”行为意味着,即使您存储的是单字节内容的字符串(例如ascii或latin1字符),使用utf8字符集声明的字符串每个字符也可以填充三个字节。同样,utf8mb4字符集会使字符串在内存中每个字符填充到四个字节。

因此,VARCHAR(255)在utf8中,存储诸如“无意见”之类的短字符串的磁盘上需要11个字节(十个低字符集字符,再加上一个字节的长度),但是在内存中则需要765个字节,因此在临时表或排序结果中也是如此。

我曾帮助MySQL用户在不知不觉中频繁创建1.5GB临时表并填满磁盘空间的情况。他们有很多VARCHAR(255)列,实际上存储很短的字符串。

最好根据要存储的数据类型定义列。如其他人所提到的,它具有强制执行与应用程序相关的约束的好处。但是它具有物理上的好处,可以避免上述的内存浪费。

当然,很难知道最长的邮政地址是什么,这就是为什么许多人选择的长度VARCHAR肯定比任何地址都要长的原因。通常使用255,因为它是a的最大长度VARCHAR,该长度可以用一个字节编码。它也是VARCHARMySQL早于5.0 的最大长度。


6
我一直认为255使用字符串的长度可以使其适合单个字节
BlueRaja-Danny Pflughoeft

3
@BlueRaja:对于内部文件结构在单个字节中编码字符串长度,或者在单个字节中编码短字符串的数据库,这可能是正确的。但是,对于大多数数据库而言,情况已不再如此。
比尔·卡文

7
@BlueRaja:InnoDB不存储以下varchar的长度,它存储行中所有字段的一系列字段偏移量。如果总行大小小于127个字节,则这些字段偏移量可以为1个字节,否则为2个字节。见forge.mysql.com/wiki/MySQL_Internals_InnoDB
Bill Karwin 2011年

6
@BlueRaja:MyISAM(对于那些仍在使用它的人)确实存储了varchar长度,这些长度可以1或2个字节存储。但是:“将索引发送到index_read()或records_in_range的处理程序时,我们总是使用2字节长的VARCHAR来简化事情。” 见forge.mysql.com/wiki/MySQL_Internals_MyISAM
Bill Karwin 2011年

1
一个问题-在任何字段或varchar字段本身上进行排序和分组?
罗希特·邦加

24

除了设置varchar大小的大小和性能方面的考虑(可能更重要的是,随着存储和处理的价格每秒钟降低),“仅因为”而使用varchar(255)的缺点是降低了数据完整性

定义字符串的最大限制是一件好事,可以防止从数据库中检索到比预期更长(更多字节)的值时,比预期更长的字符串进入RDBMS并在以后导致缓冲区溢出或异常/错误。

例如,如果您的字段接受国家/地区缩写的两个字符的字符串,那么您就没有理由期望您的用户(在这种情况下,程序员)会输入完整的国家/地区名称。由于您不希望他们输入“安提瓜和巴布达”(AG)或“赫德岛和麦当劳群岛”(HM),因此您不允许在数据库层中输入。另外,很可能有些程序员尚未对设计文档进行RTFM(肯定存在)知道不这样做。

将字段设置为接受两个字符,然后让RDBMS处理该字段(通过截断或通过拒绝错误的SQL来优雅地处理)。

没有理由超过一定长度的真实数据示例:

  • 加拿大邮政编码的格式为A1A1A1,即使是圣诞老人,长度也始终为6个字符(6个字符不包括可以为可读性指定的空格)。
  • 电子邮件地址 -@之前最多64个字节,之后最多255个字节。再也没有,免得您破坏了互联网。
  • 北美电话号码不得超过10位数字(国家/地区代码除外)。
  • 运行Windows的计算机(最新版本)的计算机名称不能超过63个字节,尽管不建议使用超过15 个字节的计算机,这将破坏Windows NT服务器场。
  • 国家的缩写是2个字符(例如上面的国家/地区代码)
  • UPS跟踪号的长度为18、12、11或9个字符。18个字符的数字以“ 1Z”开头,而11个字符的数字以“ T”开头,这使您想知道如果他们不知道字母和数字之间的区别,它们将如何交付所有这些软件包。

等等...

花点时间考虑一下您的数据及其限制。如果您是建筑师,开发人员或程序员,那毕竟是您的工作

通过使用varchar(n)而不是varchar(255),可以消除用户(最终用户,程序员,其他程序)输入意想不到的长数据,该数据稍后会困扰您的代码的问题。

我并不是说您也不应在应用程序使用的业务逻辑代码中实现此限制。


5
加拿大邮政编码实际上有7位数字,中间的空格很重要,应该在邮寄标签上显示。如果有扩展,北美电话号码可能超过10位数字。如果可以存储电话分机号没问题,那么10位数字就可以了,但是您可能会后悔。
Kibbee

3
肯定存在限制数据完整性的情况。不过,过于严格仍然很容易。对您控制的数据施加限制,并对无法控制的数据要求施加合理的限制。您的电话号码和电子邮件限制是理智的(假设您从未进行国际化)。您要求截断两个字符的国家/地区代码是“优美的”事情,这是疯狂的。您知道有错误,请不要截断并接受。如果截断,极有可能导致您输入不正确的国家/地区代码。
coderjoe

大多数应用程序在将数据发送到数据库之前都会完成数据验证...
Cobby

2
当然。最。但是我觉得您在这里假设一个正在为现有数据库开发新应用程序的开发人员知道数据的限制(我们并不是每种数据类型以及如何在每个数据库中实现数据的专家)。仅仅因为您可以验证应用程序中的数据并不意味着您可以。
shufler 2011年

3
the design documentation (which surely exists)哈哈 :D
卡米洛·马丁

14

我和你在一起。对细节的挑剔对颈部造成痛苦,并且价值有限。

以前,磁盘是一种珍贵的商品,我们过去经常用子弹来优化磁盘。存储价格下降了1000倍,这使得压缩每个字节所花费的时间变得不那么有价值了。

如果仅使用CHAR字段,则可以获取固定长度的行。如果您为字段选择了正确的大小,这可以节省一些磁盘的实时还原。您可能会获得更密集的数据(用于表扫描的I / O更少)和更快的更新(更容易在块中定位开放空间以进行更新和插入)。

但是,如果您高估了大小,或者实际数据大小是可变的,则会浪费CHAR字段来浪费空间。数据结束时的密集程度会降低(导致大型检索需要更多I / O)。

通常,尝试将大小放在变量字段上的性能收益很小。通过将VARCHAR(255)与CHAR(x)进行比较,可以轻松进行基准测试,以查看是否可以测量差异。

但是,有时候,我需要提供“小”,“中”,“大”提示。因此,我使用16、64和255作为尺寸。


13

如今,我无法想象这真的很重要。

使用可变长度字段会产生计算开销,但是由于当今CPU过多,因此甚至不值得考虑。I / O系统太慢,以至于使有效处理varchar的任何计算成本都不存在。实际上,通过在固定长度字段上使用可变长度字段,varchar的价格在计算上可能是净节省的磁盘空间。您很可能具有更大的行密度。

现在,varchar字段的复杂性在于您无法通过记录号轻松找到记录。当您具有固定长度的行大小(具有固定长度的字段)时,计算行ID指向的磁盘块很简单。在行长度可变的情况下,这种情况会消失。

因此,现在您需要维护某种记录号索引,就像其他任何主键一样,或者您需要制作一个健壮的行标识符,以将详细信息(例如块等)编码到标识符中。但是,如果执行此操作,则如果将行移动到持久性存储中,则必须重新计算ID。没什么大不了的,只需要重写所有索引条目并确保您要么a)永远不要将其公开给使用者,要么b)永远不要断言该数字是可靠的。

但是由于我们今天拥有varchar字段,因此varchar(16)相对于varchar(255)的唯一值是数据库将对varchar(16)强制执行16个char限制。如果假定DB模型实际上代表物理数据模型,则具有字段长度可能很有价值。但是,如果只是“存储”而不是“模型与存储”,则没有任何必要。

然后,您只需要区分可索引的文本字段(例如varchar)和不可索引的文本字段(例如文本或CLOB字段)。可索引字段倾向于限制大小以方便索引,而CLOB字段则没有(原因)。


5

以我的经验,如果您允许使用255个字符的数据类型,则一些愚蠢的用户(或一些经验丰富的测试人员)实际上会填满该字符。

然后,您会遇到各种各样的问题,包括您在应用程序中的报表和屏幕显示中为这些字段留出了多少空间。更不用说有可能超出数据库中数据的每行限制(如果您在这255个字符字段中有多个)。

在开始时选择一个合理的限制要容易得多,然后通过应用程序和数据库强制实施。


0

优良作法是根据需要分配少量资源。电话号码永远不会这么大。

原因之一是,除非您针对大型条目进行验证,否则无疑有人会使用其中的全部内容。这样一来,您的行可能空间不足。我不确定MySQL的限制,但8060是MS SQL中的最大行大小。

正常的默认值是50 imho,然后在需要的地方增加它。


谢谢。我绝对同意这是一种好的做法。这是我非常想澄清的性能方面
-Olly,

0

在mysql上下文中,当在所述varchar列上使用索引时,它会变得很重要,因为mysql具有最大值。每个索引行的上限为767字节。

这意味着,当在多个varchar 255列中添加索引时,您可以相当快速地达到此限制,甚至在utf8或utf8mb4列上也可以更快地达到此限制,如上面的答案所指出的那样

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.