MySQL-varchar长度和性能


Answers:


31

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

在InnoDB和MyISAM的标准行格式(动态/紧凑)中,a VARCHAR(50)和a VARCHAR(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)

因此,一般建议是使用尽可能小的类型,因为否则可能会导致性能或管理问题。即使您不知道确切的长度,A VARCHAR(100)还是比VARCHAR(255)(尽管a 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

0

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

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

关于255的问题已被询问并回答了很多次。

  • 时间太长VARCHARs会导致失败CREATE TABLE
  • 临时表可以变成MEMORY表,而VARCHARs变成VARCHAR。例如,这意味着需要VARCHAR(255) CHARACTER SET utf8mb41020字节的固定长度。(这将失败,并且将退化为使用MyISAM。)

底线:不要盲目使用255(或256);做对方案有意义的事情。

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.