MySQL VARCHAR大小的性能影响


45

MySQL在varchar大小之间是否存在性能差异?例如varchar(25)varchar(64000)。如果不是,是否有理由不声明所有具有最大大小的varchar,只是为了确保您不会用完空间?


3
+1此问题适用于所有DBMS。我观察到许多varchar大小趋于增长。
bernd_k 2011年

5
不是MySQL,但是Depesz的这篇博客文章可能会回答您的PostgreSQL问题。
xenoterracide 2011年

Answers:


29

您必须意识到使用CHAR与VARCHAR的权衡

使用CHAR字段,您分配的正是您所获得的。例如,无论您在字段中放置的字符如何,CHAR(15)都会分配并存储15个字节。字符串操作简单明了,因为数据字段的大小是完全可以预测的。

使用VARCHAR字段,您将获得完全不同的故事。例如,VARCHAR(15)实际上动态分配最多16个字节,最多动态分配15个数据,并至少分配1个额外的字节来存储数据的长度。如果要存储的字符串'hello'将占用6个字节,而不是5个字节。在所有情况下,字符串操作都必须始终执行某种形式的长度检查。

当您做两件事时,这种折衷会更加明显:
1.存储数百万或数十亿行
2.索引为CHAR或VARCHAR的列

交易#1

显然,VARCHAR拥有优势,因为可变长度数据将产生较小的行,从而产生较小的物理文件。

交易#2

由于CHAR字段由于具有固定的字段宽度而需要较少的字符串操作,因此针对CHAR字段的索引查找平均比VARCHAR字段快20%。我这不是任何猜想。《MySQL数据库设计和调优》这本书在MyISAM表上做了出色的工作来证明这一点。本书中的示例执行了以下操作:

ALTER TABLE tblname ROW_FORMAT=FIXED;

此指令强制将VARCHAR用作CHAR。我在2007年的上一份工作中做到了这一点,并使用了300GB的表,并将索引查找速度提高了20%,而没有进行任何其他更改。它像出版一样工作。但是,它的确产生了几乎两倍大的表,但这只是权衡第一。

您可以分析所存储的数据,以查看MySQL对列定义的建议。只需对任何表运行以下命令:

SELECT * FROM tblname PROCEDURE ANALYSE();

这将遍历整个表,并根据其包含的数据,最小字段值,最大字段值等为每列推荐字段定义。有时,您只需要在规划CHAR与VARCHAR时使用常识。这是一个很好的例子:

如果要存储IP地址,则该列的掩码最多为15个字符(xxx.xxx.xxx.xxx)。我会心跳一跳,直接跳到CHAR(15),因为IP地址的长度变化不会太大,而且字符串操作的复杂性由一个额外的字节控制。您仍然可以对这样的列执行PROCEDURE ANALYSE()。它甚至可能建议使用VARCHAR。在这种情况下,我的钱仍将超过VARCHAR放在CHAR上。

CHAR与VARCHAR问题只有通过适当的计划才能解决。强大的力量伴随着巨大的责任(陈词滥调,但事实如此)


4
如果您存储IP地址,那么我没有理由将它们存储为除int之外的任何其他内容。这就是IP地址。许多语言都有某种ip2int函数。如果您想方便地调用命令行,则不难制作一个存储过程来转换ABCD:A pow(256,3)+ b pow(256,2)+ c * 256 + d
atxdba 2011年

1
更错误的是,我想mysql有它自己的ip2int函数:INET_ATON
atxdba 2011年

3
@atxdba:我的答案只是使用CHAR vs VARCHAR。我仅以IP为例,因为它的字符串字符大小接近15。因此,为了解决问题本身,以VARCHAR取舍稳定的CHAR大小只是一个示例。您对表示IP地址的更好方法的评论是非常有效的,并且最有意义。
RolandoMySQLDBA 2011年

CHAR(15)分配15个字符,而不是bytes。对于utf8,即45 个字节
里克·詹姆斯

2
尽管这是关于CHAR / VARCHAR比较的一个很好的答案,但问题是关于不同的VARCHAR大小。
收藏者

13

答案实际上是相当复杂的。简短的版本:有区别

  1. 当创建临时表以过滤结果(例如GROUP BY语句)时,将分配全长。

  2. 有线协议(向客户端发送行)可能会分配更大的长度。

  3. 存储引擎可能/可能未实现适当的varchar。

对于(2),我承认我并不是很熟悉wire协议,但是这里的一般建议是尝试并至少花一些力气来猜测长度。


值得指出。MySQL 5.7可以将值打包在排序缓冲区(可变长度)中。解释这里更详细: mysqlserverteam.com/...
摩根Tocker

9

该线程中的大多数答案都使用5年,是在InnoDB和utf8为默认值之前编写的。所以,让我重新开始...

当查询需要内部临时表时,它将尝试使用MEMORY表。但是如果以下情况不能使用MEMORY

  • TEXT/ BLOB正在获取的列,甚至没有TINYTEXT
  • VARCHAR 大于某个数量,在当前版本中可能为512。

另外,请注意VARCHARs将变成了CHARs。因此,VARCHAR(255)CHARACTER SET utf8膨胀到765个字节,无论是什么在列。然后,这可能会触发:

  • 如果MEMORY表大于max_heap_table_size tmp_table_size,它将转换为MyISAM并可能溢出到磁盘。

因此,VARCHAR(25)更有可能停留MEMORY,从而更快。 (255)不好,也(64000)很糟糕。

(将来,临时表可能是InnoDB,并且此答案的一部分将需要修改。)


6

大小的varchar列使整个表上的查询更有可能使用临时表。根据高性能MySQL的书。当优化器尝试查看它是否可以在内存中运行此查询或是否需要临时表时,它会根据表定义查看行大小,这意味着,为了提高速度,它不会尝试查看64K字符中的多少您实际上在使用。这就是为什么作者建议您不要将该定义方式扩展到超出该列中的实际可能值的范围。显然,如果您为进入临时表的更多查询做好了准备(即使实际数据大小可以容纳在RAM中),那么您现在可能会遭受I / O损失。


这是一个非常新鲜的观点。如果这是您要参考的书(amazon.com/MySQL-High-Availability-Building-Centers/dp/…),请在您的答案中放入该书的页码,因为我想阅读。+1 !!!
RolandoMySQLDBA 2011年

愚蠢的我…高性能而不是可用性:amazon.com/High-Performance-MySQL-Optimization-Replication/dp/……页数是236/237它说明了定义varchar列的慷慨做法是多么不明智。请记住,尽管这本书是在5.1发布时才写的。明年将发布第三版,其中包含5.5中的所有BIG更改,因此可能会更改:)
TechieGurl 2011年

页面236提到了属于特定字符集的归类。对于VARCHAR来说,这可能有点讨厌。在第237页上,客户端/服务器通信的设置以及第238页上的图5-5显示了另一个原因。来回转换字符集的过程。再次,VARCHAR的另一个令人讨厌的冒险。
RolandoMySQLDBA 2011年

为了澄清起见,即使本节没有完全说出MySQL将使用create size,我们知道当一个操作需要一个临时表时,该表位于MEMORY Engine中,并且该字符串始终以修复块的形式存储字符串,因此这就是慷慨的方式定义可能导致所需的MEMORY临时表进入磁盘,而不是停留在RAM中
TechieGurl 2011年

@RolandoMySQLDBA。是的…在此排序规则也成为一个因素(特别是如果您使用UTF-8且具有非拉丁字符),并且在处理内存引擎表时,它们只会杀死ya,并导致磁盘访问速度更快
TechieGurl

5

据我了解,较小的字段可能直接包含在索引中,而较长的字段则不能。由于该限制,如果您希望字符串可索引,请说使它们更短。否则,不然,无论字段是25还是MAX,因为它们都是varchar,然后像排序或比较之类的操作将在相同的时间内运行。


3

确保您没有空间用完

这个短语意味着您问这个问题,因为您不确定要存储在数据库中的数据。如果是这样,那么您将尽快找到原因,因为在进行容量规划时需要用到它。例如,如果您要获取具有7000个字符的数据元素,则需要知道,因为这会对任何DBMS产生性能影响。

也就是说,我希望列大小与预期内容相关。例如,即使您包括国家代码和分机号,电话号码也不可能超过50个字符。同样,邮政编码或邮政编码最可能为20个字符或更少。

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.