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

时间:2008-11-04 16:03:15

标签: mysql sql

我有一个contacts表,其中包含postcodefirst namelast nametowncountry,{{1}等字段等等,所有这些都定义为phone number,即使这些字段都不会接近255个字符。 (如果你想知道,就是这样,因为Ruby on Rails迁移默认情况下将字符串字段映射到VARCHAR(255)并且我从不打扰覆盖它。

由于VARCHAR只会存储字段的实际字符数(以及字段长度),因此使用VARCHAR(255)优于VARCHAR(16)是否有明显的优势(性能或其他方面) ?

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

仅供参考我正在使用MySQL 5.

7 个答案:

答案 0 :(得分:125)

在存储中,VARCHAR(255)非常智能,只能存储您在给定行上所需的长度,而CHAR(255)总是存储255个字符。

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

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

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

所以utf8中的VARCHAR(255)存储一个像“No opinion”这样的短字符串,在磁盘上需要11个字节(10个低字符集字符,加上一个字节表示长度),但它在内存中需要765个字节,因此在临时表或排序结果。

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

最好根据您要存储的数据类型定义列。正如其他人提到的那样,它有助于强制执行与应用程序相关的约束。但它具有避免我上面描述的内存浪费的物理好处。

很难知道最长的邮政地址是什么,当然,这就是为什么很多人选择长VARCHAR的地址肯定比任何地址长。并且255是惯用的,因为它是VARCHAR的最大长度,其长度可以用一个字节编码。它也是MySQL中早于5.0的最大VARCHAR长度。

答案 1 :(得分:24)

除了设置varchar大小的大小和性能考虑因素(可能更重要的是,随着存储和处理每秒变得更便宜),使用varchar(255)“仅仅因为”的缺点会减少数据完整性

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

例如,如果您有一个字段接受国家/地区缩写的双字符字符串,那么您无法想象您的用户(在此上下文中是程序员)输入完整的国家/地区名称。由于您不希望它们进入“安提瓜和巴布达”(AG)或“赫德岛和麦当劳群岛”(HM),因此您不允许它们进入数据库层。此外,一些程序员可能还没有RTFMed设计文档(肯定存在)知道不这样做。

将字段设置为接受两个字符并让RDBMS处理它(通过截断或不正确地拒绝它们的SQL并带有错误来优雅地处理它)。

无理由超过一定长度的实际数据示例:

  • Canadian Postal Codes的格式为A1A1A1,长度始终为6个字符,即使是圣诞老人(6个字符也不包括可以指定易读性的空格)。
  • email addresses - @之前最多64个字节,之后最多255个字节。永远不要更多,以免你打破互联网。
  • 北美电话号码不得超过10位数(不包括国家/地区代码)。
  • 运行(最新版本)Windows的计算机不能拥有computer names longer than 63 bytes,但不建议使用超过15个,这将破坏您的Windows NT服务器场。
  • 州名缩写为2个字符(如上面示例的国家/地区代码)
  • UPS tracking numbers长度为18-,12-,11-或9个字符。 18个字符的数字以“1Z”开头,11个字符的数字以“T”开头,这让您想知道如果他们不知道字母和数字之间的区别,他们如何提供所有这些包。

等等......

花点时间考虑一下您的数据及其限制。毕竟,如果您是架构师,开发人员或程序员,那么这就是您的职位

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

我并没有说你不应该在你的应用程序使用的业务逻辑代码中实现这个限制。

答案 2 :(得分:13)

我和你在一起。对细节的挑剔是颈部疼痛,价值有限。

曾几何时,磁盘是一种珍贵的商品,我们常常用子弹来优化磁盘。存储价格下降了1000倍,使得压缩每个字节的时间变得不那么有价值了。

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

但是,如果您高估了您的尺寸,或者您的实际数据大小是可变的,那么您最终会在CHAR字段中浪费空间。数据封装得不那么密集(导致大量检索的I / O更多)。

通常,尝试在变量字段上放置大小的性能优势很小。与CHAR(x)相比,您可以使用VARCHAR(255)轻松进行基准测试,看看是否可以测量差异。

然而,有时,我需要提供“小”,“中”,“大”的提示。所以我使用16,64和255作为尺寸。

答案 3 :(得分:13)

如今,我无法想象它真的重要了。

使用可变长度字段会产生计算开销,但是如今CPU过多,甚至不值得考虑。 I / O系统非常慢,以至于无法有效地处理varchars的任何计算成本。事实上,varchar的价格在计算上可能是通过在固定长度字段上使用可变长度字段而节省的磁盘空间量的净胜利。你很可能有更大的行密度。

现在,varchar字段的复杂性在于您无法通过它的记录号轻松找到记录。当您具有固定长度的行大小(具有固定长度字段)时,计算行id指向的磁盘块是微不足道的。对于可变长度行数,这种情况会超出窗口。

因此,现在您需要维护某种记录号索引,就像任何其他主键一样,或者您需要制作一个强大的行标识符,用于对标识符中的详细信息(例如块等)进行编码。但是,如果这样做,则必须重新计算id,如果行在持久存储上移动的话。没什么大不了的,只需要重写所有索引条目并确保你要么a)永远不要将它暴露给消费者,要么b)永远断言这个数字是可靠的。

但由于我们今天有varchar字段,因此varchar(16)与varchar(255)的唯一值是DB将对varchar(16)强制执行16个字符串限制。如果假设DB模型实际上代表物理数据模型,则具有字段长度可以是有价值的。但是,如果它只是“存储”而不是“模型和存储”,那就没有必要了。

然后,您只需要在可索引的文本字段(例如varchar)与不可文件的字段(如文本或CLOB字段)之间进行区分。可索引字段往往对大小有限制以便于索引,而CLOB字段则没有(在合理范围内)。

答案 4 :(得分:5)

根据我的经验,如果你允许255个字符的数据类型,一些愚蠢的用户(或一些有经验的测试人员)实际上会填满它。

然后您会遇到各种各样的问题,包括您在应用程序的报告和屏幕显示中允许这些字段的空间大小。更不用说超出数据库中数据的每行限制的可能性(如果你有超过255个字符字段中的一些)。

在开始时更容易选择合理的限制,然后通过应用程序和数据库强制执行。

答案 5 :(得分:0)

最好只分配一些你需要的东西。电话号码永远不会那么大。

一个原因是,除非您对大型条目进行验证,否则有人会使用所有条目。那么你的行可能会用完空间。我不确定MySQL限制,但8060是MS SQL中的最大行数。

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

答案 6 :(得分:0)

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

这意味着当在几个varchar 255列中添加索引时,您可以在utf8或utf8mb4列上更快/更快地达到此限制,如上面的答案所述