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

评论

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

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

#1 楼

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

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

当您执行两个操作时,权衡更为明显事情:
1。存储数百万或数十亿行
2。索引为CHAR或VARCHAR的列

TRADEOFF#1

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

TRADEOFF#2

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

ALTER TABLE tblname ROW_FORMAT=FIXED;


该指令强制将VARCHAR用作CHAR。我在2007年的上一份工作中做到了这一点,并使用了300GB的表,并将索引查找速度提高了20%,而没有进行任何其他更改。它像出版一样工作。但是,它的确产生了一个表,其大小几乎增加了一倍,但这可以追溯到权衡#1。您可以分析所存储的数据,以查看MySQL对列定义的建议。只需对任何表运行以下命令:

SELECT * FROM tblname PROCEDURE ANALYSE();


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

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

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

评论


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

– atxdba
2011-10-18 2:50



更错误的是我猜mysql有它自己的ip2int函数:INET_ATON

– atxdba
2011-10-18的3:08

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

– RolandoMySQLDBA
2011年10月18日,下午3:13

CHAR(15)分配15个字符,而不是字节。对于utf8,即45个字节。

–里克·詹姆斯(Rick James)
2012年4月5日19:53

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

–收藏家
16年2月5日,下午2:34

#2 楼

答案实际上是相当复杂的。简短版本:有所不同。


创建临时表以过滤结果时(例如GROUP BY语句),将分配全长。
wire协议(发送行到客户端)可能会分配更大的长度。
存储引擎可能会/可能不会实现适当的varchar。

对于(2),我承认有线协议不是我所需要的东西非常熟悉,但是这里的一般建议是尝试并至少花一点点力气来猜测长度。

评论


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

–摩根·托克(Morgan Tocker)
2014年2月7日15:15

#3 楼

该线程中的大多数答案都已经有五八年了,它们是在InnoDB和utf8成为默认值之前编写的。因此,让我重新开始...
当查询需要内部临时表时,它将尝试使用MEMORY表。但是,如果要提取TEXT / BLOB列,甚至TINYTEXT,也不能使用MEMORY。
VARCHAR大于某个数量,在当前版本中可能是512。

另外,请注意,将VARCHARs转换为CHARs。 (8.0对此进行了修改。)因此,不管列中包含什么内容,带有VARCHAR(255)CHARACTER SET utf8都会扩展到765个字节。然后,这可能会触发:

如果MEMORY表变得大于max_heap_table_sizetmp_table_size,它将被转换为MyISAM并可能溢出到磁盘。

因此,VARCHAR(25)更可能留下MEMORY,因此速度更快。 (255)不好,而(64000)不好。
(将来,临时表可能是InnoDB,并且此答案的一部分将需要修改。)
(更新)MySQL 8.0.2: TempTable存储引擎取代了MEMORY存储引擎,成为内存内部临时表的默认引擎。TempTable存储引擎为VARCHAR和VARBINARY列提供了有效的存储。” (从那时起,临时表的处理有了进一步的变化;我怀疑尘埃尚未解决。)

#4 楼

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

评论


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

– RolandoMySQLDBA
2011-10-19 17:25



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

– TechieGurl
2011-10-20 15:49



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

– RolandoMySQLDBA
2011-10-20 15:59



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

– TechieGurl
2011年10月20日16:06

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

– TechieGurl
2011年10月20日16:08

#5 楼

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

#6 楼


确保您不会用完空间


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

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