声明VARCHAR尺寸对性能有意义吗? VARCHAR(50)VARCHAR(255)之间(速度)有什么不同吗?还是定义长度是逻辑/设计约束?

评论

dba.stackexchange.com/questions/424/…

#1 楼

这是一个非常常见的“考试/面试题”。我将尽我所能回答:

在InnoDB和MyISAM(动态/紧凑)的标准行格式中,VARCHAR(50)VARCHAR(255)将以相同的方式存储字符串文本-1字节用于长度和每个字符1到4个字节之间的实际字符串(取决于编码和存储的实际字符)。

实际上,如果我没记错的话,我记得有人用十六进制编辑器,以便将类似VARCHAR(50)的内容更改为VARCHAR(100),因此可以动态完成(通常需要进行表重建)。这是有可能的,因为实际数据不受此更改的影响。

VARCHAR(256)则不是如此,因为这样一来,长度总是至少需要2个字节。

,那意味着我们应该总是做VARCHAR(255),不是吗?否。有几个原因。
InnoDB可能以动态方式存储varchar,而其他引擎则不然。 MyISAM具有固定的行大小格式,并且MEMORY表的大小始终是固定的。我们应该关心其他引擎吗?是的,我们应该这样做,因为即使不直接使用它们,MEMORY表也经常用于中间结果(内存中的临时表),并且由于事先不知道结果,因此必须以最大大小创建表可能-如果这是我们的类型,请输入VARCHAR(255)。如果您可以考虑浪费的空间,那么如果我们使用MySQL的'utf8' charset编码,则MEMORY将为每行的长度+ 3 * 255字节保留2个字节(对于在InnoDB上可能只需要几个字节的值)。在100万张表上,这几乎是1GB-仅用于VARCHAR。这不仅会导致不必要的内存压力,而且可能会激起要在磁盘上执行的操作,从而有可能使速度降低数千倍。所有这些都是由于对其定义的数据类型(与内容无关)的选择不佳。

它也对InnoDB产生一些后果。索引大小限制为3072字节,单列索引限制为767字节*。因此,很可能无法完全索引VARCHAR(255)字段(假设您使用utf8或任何其他可变长度编码)。

另外,InnoDB的最大内联行大小是半页(大约8000个字节),如果长度不可变的字段(例如BLOB或varchar)不适合半页,则可以将它们存储在页外。这会对性能产生某些影响(有时视情况而定,有时是好的,有时是不好的),这是不容忽视的。这在COMPACT和DYNAMIC格式之间引起了一些怪异。例如,请参见:错误1118:行大小太大。 utf8 innodb

最后但并非最不重要的是,正如@ypercube提醒我的那样,即使您使用VARCHAR(255),也可能需要超过1个字节的长度,因为定义是字符形式的,而长度存储个字节。例如,REPEAT('ñ', 255)在utf8中有超过2 ^ 255个字节,因此存储其长度需要超过1个字节:

mysql> SELECT LENGTH(REPEAT('ñ', 255));
+---------------------------+
| LENGTH(REPEAT('ñ', 255))  |
+---------------------------+
|                       510 |
+---------------------------+
1 row in set (0.02 sec)

mysql> SELECT CHAR_LENGTH(REPEAT('ñ', 255));
+--------------------------------+
| CHAR_LENGTH(REPEAT('ñ', 255))  |
+--------------------------------+
|                            255 |
+--------------------------------+
1 row in set (0.00 sec)


因此,一般性建议是使用最小的类型,因为否则可能会导致性能或管理问题。即使您不知道确切的长度,VARCHAR(100)也比VARCHAR(255)好(尽管VARCHAR(20)会更好)。尽量保持保守,因为除非表太大,否则以后总是可以更改定义。

更新:由于可变长字符串的爆炸式增长,例如,使用表情符号, Oracle一直在努力提高这些情况的性能。在最新的MySQL版本(5.6、5.7)中,InnoDB已被设置为固有表和显式临时表的默认引擎,这意味着可变长度字段现在是一等公民。这意味着可能没有太多理由限制字符长度(但是仍然存在)。

(*)第二次更新:默认情况下,最新的MySQL版本(8.0)默认启用large_prefix_index,但是对于较早的版本或使用lagacy innodb文件/行格式(动态或压缩格式除外)的情况仍然如此。默认情况下,单列索引最多可以包含3072个字节。

评论


小更新:MySQL-8.0.13 +默认将TempTable用于临时表,该临时表可有效存储varchars。

– danblack
19年1月14日在22:25

#2 楼

忘记VARCHARs上的1字节和2字节前缀。


它对性能的影响很小。
比明显的规则所说的多2。

关于255的问题已经问了很多遍。


过长的VARCHARs会导致CREATE TABLE失效。
一个复杂的SELECT例如,将使用临时表对ORDER BY进行排序。在某些情况下使用MEMORY表。在其他情况下,将使用MyISAM。使用MEMORY时,将VARCHARs转换为CHARs(用于温度表)。例如,这意味着VARCHAR(255) CHARACTER SET utf8mb4想要1020字节的固定长度。而且使用MEMORY太大了,因此它将切换到效率较低的MyISAM。

(在MySQL 8.0中,临时表的详细信息已更改。上一段适用于所有表8.0之前的引擎。)

底线:请不要盲目使用255(或256);做对方案有意义的事情。如果确实需要255(或1024或其他任何值),请继续使用它。我只是指出了一些缺点。

性能会受到多少影响?很难预测;通常,这不值得担心。 (问题是关于性能的,我试图列出所有在VARCHAR中的数字都很重要的情况,甚至一点点。)

评论


您能否详细说明“这将失败,并且将退化为使用MyISAM”?如果我将表的引擎指定为InnoDB,那么您说MySQL可以覆盖它吗?还是仅用于内存表?我之所以这样问是因为尽管有我的所有抵制,但由于客户的抱怨,我最近仍需要将VARCHAR(255)字段增加到VARCHAR(1024),我正在为性能做准备。

–Csaba Toth
20 Mar 18 '20 at 4:54

@CsabaToth-嗯;我的一些话语很草率。我做了一些重要的改写,希望会更好。并希望能解决您的问题。

–里克·詹姆斯(Rick James)
20 Mar 18 '20 at 18:47

谢谢,我会再消化一次。我们的产品基于LTS Ubuntu Server衍生产品,以及8.0之前版本的MySQL ...

–Csaba Toth
20 Mar 18 '20 at 19:01

@CsabaToth-如果您需要进一步讨论,请开始一个新的问题;在现有问题上piggy带是不恰当的。 (对于您质疑我的措辞并对其进行修正非常合适。)

–里克·詹姆斯(Rick James)
20 Mar 18 '20在22:28