MySQL表中varchar长度的重要性


112

我有一个MySQL表,其中动态插入行。因为我不能确定字符串的长度,也不希望它们被截断,所以我将它们设为varchar(200),通常比我需要的大得多。给varchar字段提供比所需长度更长的长度是否会对性能产生重大影响?


VARCHAR(255) utf8mb4具有约15万行的单个索引列的表的大小为11.5MB。VARCHAR(48) utf8mb4具有相同数据(最大长度为46个字符)的索引列的表使用4.5MB。在查询中并没有太大的区别,它已建立索引。但是它确实与查询I / O和数据库备份之类的东西加在一起。
Code4R7

Answers:


59

不,在某种意义上说,如果您存储在该列中的值始终(例如)少于50个字符,则将该列声明为varchar(50)varchar(200)具有相同的性能。


9
并非完全正确。请参见Bill Karwin
hejdav '16

5
我认为类似的答案应该得到文档,基准测试或类似的支持。
Gokhan Sari

301

这可能会对性能产生影响:在MySQL中,临时表和MEMORY表将VARCHAR列存储为固定长度的列,并填充为其最大长度。如果您设计的VARCHAR列比所需的最大尺寸大得多,则会消耗更多的内存。这会影响缓存效率,排序速度等。


33
+1。我还似乎有些JDBC驱动程序,在设置缓冲区以检索行时会为最大大小分配足够的空间。不用说,当某些小丑刚做完varchar(50000)时,以防万一某人的姓氏非常大,这会引起极大的焦虑和咬牙切齿:-)
paxdiablo

21
+1。这是一个重要的影响,我相信这是这个问题的真正答案。
Emre Yazici

6
该答案和已接受的答案都是理解OP正确答案所必需的。
kd8azz

2
实际上,当MEMORY认为该表太大时,会将其写入磁盘,从而导致性能显着下降。
Timo 2014年

1
这个答案可以通过指定它是正确的存储引擎来实现(我注意到dev.mysql.com/doc/refman/8.0/en/…表示临时表从MySQL 8开始一直是InnoDB;这会改变什么吗?) ,以及指向支持其提出的主张的文档的链接。从我在Stack Exchange上看到的输出来看,我相信您在撰写本文时是对的,但是情况可能已经改变,链接既可以为他人树立榜样,又可以帮助我们其他人找到为自己提供这类信息。
Mark Amery

14

VARCHAR适合您描述的情况,因为它的全称是“可变字符” -限制,根据你的榜样,是200个字符,但东西少被接受不能解决列的分配大小。

VARCHAR还占用较少的空间-值存储为一字节或两字节长度的前缀以及数据。长度前缀指示值中的字节数。如果值要求不超过255个字节,则一列使用一个长度字节;如果值可能需要不超过255个字节,则一列使用两个长度字节。

有关比较MySQL CHAR和VARCHAR数据类型的更多信息,请参见此链接


1
每个对MySQL存储感兴趣的人(关于CHAR和VARCHAR)都应该阅读此答案中提到的链接。谢谢!
Pascal

14

尺寸就是性能!尺寸越小越好。并非今天或明天,但总有一天,无论您采用何种设计,遇到严重瓶颈时,您的桌子都会变大。但是,您可以预见在设计阶段可能首先出现的一些潜在瓶颈,并尝试延长数据库将快速而愉快地执行的时间,直到您需要重新考虑方案或通过添加更多服务器来水平扩展时为止。

在您的情况下,您可能会遇到许多性能泄漏:长varchar列几乎不可能实现大联接。在这些列上建立索引是真正的杀手er。您的磁盘必须存储数据。一个内存页可以容纳更少的行,并且表扫描的速度将慢得多。而且查询缓存将不太可能在这里为您提供帮助。

您必须问自己:每年可能发生多少次插入?平均长度是多少?我是否真的需要200个以上的字符,还是可以通过告知用户最大长度来在我的应用程序前端中捕获这些字符?我可以将表拆分为一个狭窄的表,以进行快速索引和扫描,而另一个表则可以容纳其他的,不经常使用的,扩展大小的数据吗?我可以将可能的varchar数据键入类别,然后将一些数据提取到一些较小的列(可能是int或bool类型的列)中,然后以这种方式缩小varchar列吗?

您可以在这里做很多事情。最好先做一个假设,然后使用实际测量的性能数据逐步设计。祝好运。


+1以列出设计选项并探索影响。对我的问题也很有帮助。 stackoverflow.com/q/12083089/181638
阿萨德·易卜拉欣

5
设置高的最大长度会对性能产生实际影响,还是仅由实际尺寸决定性能?
poolie 2013年

5

性能?否。磁盘存储?是的,但是很便宜而且很多。除非您的数据库将增长到TB级,否则您可能还可以。


奇怪的是,这个答案在发布六年后就被否决了,其他答案都没有。似乎斗气和小气。这个答案没有错。版主?
duffymo '16

1
如前所述,它确实会影响性能。同样,磁盘存储也不是免费的。较宽的列意味着更多的磁盘读/写(并且可访问磁盘),并且索引也更宽,从而降低了它们的实用性。两者都会对性能产生负面影响。也许在一个小型数据库中这可以忽略不计,但是正如您所说的那样,在千兆字节/兆字节规模上,这无疑是很重要的。对于100个寄存器表,这无关紧要。
亚历杭德罗(Alejandro)

5

你们中有些人错误地认为a varchar(200)在磁盘上所占的表大小比a在磁盘上所占的表大varchar(20)。不是这种情况。只有当您超过255个字符时,mysql才会使用一个额外的字节来确定varchar字段数据的长度。


9
临时表和临时表不是这样MEMORY
Lightness Races in Orbit Race

4
每当您的选择查询使用临时表(按操作进行分组和排序等)时,它将把varchar(200)转换为char(200),从而降低性能。
杰米

1

可能会影响性能-但通常不会达到大多数用户会注意到的水平。

当预先知道每个字段的大小时,MySQL会确切知道每个字段/行之间有多少字节,并且可以在不读取所有数据的情况下进行页面前移。使用可变字符会削弱这种优化能力。

varchar是否会由于数据碎片而导致性能下降?

更好的是char vs varchar

对于大多数用途,你会被罚款或者用-但有一个区别,而对于大型数据库,是有原因的,你会选择一个或另一个。


0

大小是varchar,而不仅仅是char,它的大小基于内部字段来指示其实际长度和字符串本身。因此,使用varchar(200)与使用varchar(150)并没有太大区别,只是您有潜力存储更多内容。

而且,当行增长时,您应该考虑在更新上会发生什么。但是,如果这种情况很少见,那您应该没事。


0

根据数据类型名称表明这是VARCHAR,即变量chars数据存储,mysql引擎本身根据存储的数据分配正在使用的内存,因此据我所知没有性能受到影响。


0

您应该尝试在大多数情况下以与char列相同的方式查看varchar列,并保守地设置长度。您不必总是这么想var修饰符,因为它会影响最大长度上的决策。确实应该将其视为性能提示,而不是所提供的字符串长度会有所不同。

这不是必须由数据库内部严格遵循的指令,可以完全忽略它。但是请务必小心,因为有时实现可能会泄漏(例如,固定长度和填充),即使它不在理想的世界中也是如此。

如果您使用的是varchar(255),则无法保证在性能方面总是在任何情况下都与char(255)有所不同。

按照手册中有关存储要求的建议,将其设置为255、65535等似乎很容易。这给人的印象是0(是的,是东西)和255之间的任何值都会产生相同的影响。但是,这不能完全保证。

对于行存储而言,存储要求确实是真实的,或者是对于成熟持久的存储引擎的良好指示。它不能很好地指示诸如索引之类的指标。

有时候这是一个棘手的问题,确切地说,一条弦应该持续多长时间,以便将其设置为您应该知道的最大范围,但这没有影响。不幸的是,这通常留给用户解决,这确实有些武断。您真的不能说永远不要使字符串过大,因为在某些情况下您可能不确定。

您应该确保当字符串太长而不是截断字符串时,MySQL查询会引发错误,以便至少知道字符串是否因发出错误而太短。调整列大小以放大或缩小它们可能是昂贵的DDL操作,应牢记这一点。

字符集还应考虑长度和性能的影响。长度是指长度而不是字节。例如,如果使用utf8(而不是MB4),则varchar(255)实际上是varbinary(3 * 255)。如果不运行测试并深入研究源代码/文档,很难知道这样的事情将如何发挥作用。因此,存在过长的长度以产生意外膨胀的影响的可能性。这不仅适用于性能。如果您有一天需要将varchar列的字符集更改为较大的字符集,那么如果允许出现不必要的长字符串,您可能最终无济于事而无法达到要求。通常这是一个相当小众的问题,但确实存在,

如果事实证明MAX(LENGTH(column))总是<64(例如,如果确定输入的限制与列定义不匹配),但是您有varchar(255),则有一个您很有可能会使用比某些情况下所需空间多四倍的空间。

这可能包括:

  • 不同的引擎,有些人可能会完全忽略它。
  • 缓冲区大小(例如更新或插入)可能必须分配完整的255(尽管我没有检查源代码来证明这一点,但这只是一个假设)。
  • 索引,如果您尝试从许多varchar(255)列中创建复合键,这将立即显而易见。
  • 中间表和可能的结果集。考虑到事务的工作方式,某件事可能并非总是可以使用一列中字符串的实际最大长度而不是所定义的限制。
  • 内部预测性优化可能会将最大长度作为输入。
  • 数据库实现版本中的更改。

根据经验,实际上不需要使varchar的长度超过需要的长度,无论是否存在性能问题,因此我建议您在可能的情况下坚持使用。理想的方法是花更多的精力来采样数据大小,实施真正的限制或通过询问/研究找出真正的限制。

当您无法执行此操作时,如果您想对不确定的情况执行诸如varchar(255)之类的操作,那么我建议您进行科学操作。这可能包括复制表,减小var char列的大小,然后将数据从原始表复制到表中以及查看索引/行数据的大小(也对列进行索引,也可以尝试将其用作主键,在InnoDB中,行为可能会有所不同,因为行是按主键排序的)。至少通过这种方式,您将知道您是否会对IO产生影响,而IO往往是最敏感的瓶颈之一。测试内存使用情况比较困难,很难对其进行详尽的测试。我建议测试潜在的最坏情况(在内存结果中有很多中间查询,请向大型临时表进行解释,等等)。

如果您知道表中不会有太多行,就不会将列用于联接,索引(尤其是复合,唯一)等,那么您很可能不会遇到很多问题。

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.