MySQL - varchar 长度和性能

Son*_*que 31 mysql database-design

声明VARCHAR大小对性能有意义吗?是否有任何区别(速度)VARCHAR(50)VARCHAR(255)?或者定义长度是逻辑/设计约束?

jyn*_*nus 40

这是一个非常常见的“考试/面试问题”。我会尽量回答:

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

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

情况并非如此VARCHAR(256),因为总是需要 2 个字节(至少)作为长度。

所以,这意味着我们应该总是这样做VARCHAR(255),不是吗?不,有几个原因。

虽然 InnoDB 可能以动态方式存储 varchar,但对于其他引擎则不然。MyISAM 具有固定的行大小格式,而 MEMORY 表的大小始终是固定的。我们应该关心那些其他引擎吗?是的,我们应该,因为即使我们不直接使用它们,MEMORY 表也非常常用于中间结果(内存上的临时表),并且由于事先不知道结果,因此必须以最大大小创建该表可能 -VARCHAR(255)如果那是我们的类型。如果可以考虑浪费的空间,如果我们使用MySQL的'utf8' charset编码,MEMORY会为长度保留2个字节+每行3 * 255个字节(对于在 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)
Run Code Online (Sandbox Code Playgroud)

所以一般的建议是尽可能使用最小的类型,因为否则它可能会产生性能或管理问题。AVARCHAR(100)优于VARCHAR(255)(尽管 aVARCHAR(20)会更好),即使您不知道确切的长度。尽量保守一点,因为除非表太大,否则您以后可以随时更改定义。

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

(*)第二次更新:现在在最新的 MySQL 版本 (8.0) 上默认启用 large_prefix_index,但对于旧版本或者如果您使用滞后的 innodb 文件/行格式(动态或压缩除外),这仍然是正确的,但现在默认情况下,单列索引最多可达 3072 个字节。