MySQL VARCHAR 大小的性能影响

Ben*_*enV 55 mysql performance datatypes varchar

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

Rol*_*DBA 34

您必须意识到使用 CHAR 与 VARCHAR 的权衡

使用 CHAR 字段,您分配的内容正是您获得的内容。例如,CHAR(15) 分配和存储 15 个字节,无论您在字段中放置多少字符。字符串操作简单明了,因为数据字段的大小是完全可以预测的。

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

当你做两件事时,权衡更明显:
1. 存储数百万或数十亿行
2. 索引 CHAR 或 VARCHAR 列

权衡#1

显然,VARCHAR 具有优势,因为可变长度数据会产生更小的行,从而产生更小的物理文件。

权衡#2

由于 CHAR 字段由于字段宽度固定而需要较少的字符串操作,因此针对 CHAR 字段的索引查找平均比 VARCHAR 字段快 20%。这不是我的任何猜测。《MySQL Database Design and Tuning》一书在 MyISAM 表上做了一些奇妙的事情来证明这一点。书中的例子做了如下的事情:

ALTER TABLE tblname ROW_FORMAT=FIXED;
Run Code Online (Sandbox Code Playgroud)

该指令强制 VARCHAR 的行为与 CHAR 相同。我在 2007 年的上一份工作中做到了这一点,并使用了一个 300GB 的表并将索引查找速度提高了 20%,而没有更改任何其他内容。它按发布的方式工作。然而,它确实产生了一张几乎两倍大小的表,但这只是回到权衡#1。

您可以分析存储的数据以查看 MySQL 推荐的列定义。只需对任何表运行以下命令:

SELECT * FROM tblname PROCEDURE ANALYSE();
Run Code Online (Sandbox Code Playgroud)

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

如果要存储 IP 地址,则此类列的掩码最多为 15 个字符 (xxx.xxx.xxx.xxx)。我会立即跳到 CHAR(15) 处,因为 IP 地址的长度不会有太大变化,并且字符串操作的复杂性由附加字节控制。您仍然可以对这样的列执行 PROCEDURE ANALYSE()。它甚至可能会推荐 VARCHAR。在这种情况下,我的钱仍然会放在 CHAR 上而不是 VARCHAR 上。

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

  • 如果您存储 IP 地址,我认为没有理由将它们存储为 int 以外的任何内容。这就是IP地址的全部内容。许多语言都有某种 ip2int 函数。如果您想要命令行调用的便利性,那么制作一个存储过程来转换 ABCD 并不难: A*pow(256,3)+b*pow(256,2)+c*256+d (5认同)
  • @atxdba:我的回答重点是使用 CHAR 与 VARCHAR。我仅以 IP 为例,因为它的字符串字符大小接近 15。因此,为了问题本身,舍入稳定的 CHAR 大小以支持 VARCHAR 只是一个示例。您关于表示 IP 地址的更好方法的评论非常有效并且最有意义。 (3认同)
  • 虽然这是关于 CHAR / VARCHAR 比较的一个很好的答案,但问题是关于不同的 VARCHAR 大小。 (3认同)

Ric*_*mes 15

大多数在这个线程的答案是5八岁,之前写的InnoDB和UTF-8是默认值。那么,让我重新开始……

当查询需要一个内部临时表时,它会尝试使用一个MEMORY表。但是 MEMORY 不能使用,如果

  • TEXT/BLOB列被提取,甚至TINYTEXT.
  • VARCHAR 大于某个数量,在当前版本中可能是 512。

另外,请注意VARCHARs变成了CHARs. (8.0 对此进行了修改。)因此,无论列中是什么VARCHAR(255),a 都会CHARACTER SET utf8扩展到 765 个字节。然后,这可能会被触发:

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

所以,VARCHAR(25)更有可能留下来MEMORY,因此会更快。 (255)没有那么好,而且(64000)很糟糕。

(将来,临时表可能会是InnoDB,并且此答案的一部分需要修改。)

(更新)MySQL 8.0.2:“TempTable 存储引擎取代 MEMORY 存储引擎作为内存内部临时表的默认引擎。TempTable 存储引擎为 VARCHAR 和 VARBINARY 列提供高效存储。” (从那时起,临时表的处理发生了进一步的变化;我怀疑还没有尘埃落定。)


Mor*_*ker 14

这个问题的答案实际上相当复杂。简短版本:有区别

  1. 创建临时表来过滤结果(例如GROUP BY语句)时,将分配全长。

  2. 有线协议(向客户端发送行)可能会分配更大的长度。

  3. 存储引擎可能/可能不会实现适当的 varchar。

对于 (2) 我承认有线协议不是我非常熟悉的东西,但这里的一般建议是尝试至少应用一些最小的努力来猜测长度。


Tec*_*url 6

一个 varchar 列的大小使得对整个表的查询更有可能使用临时表。根据高性能 MySQL 书。当优化器尝试查看它是否可以在内存中运行此查询或是否需要临时表时,它会根据表定义查看行大小,这意味着为了速度,它不会尝试查看 64K 字符中有多少你实际上正在使用。这就是作者建议您不要将该定义扩展到超出列中实际可能值的原因。显然,如果您为进入临时表的更多查询设置自己(即使实际数据大小可以容纳在 RAM 中),您现在已经招致了本可以避免的 I/O 损失。


jco*_*and 5

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


小智 4

确保你不会用完空间

这句话意味着您提出这个问题是因为您不确定将存储在数据库中的数据。如果这是真的,那么您将尽快找到答案,因为您将需要它来进行容量规划。例如,如果您可能获取包含 7000 个字符的数据元素,您需要知道,因为这会对任何 DBMS 产生性能影响。

也就是说,我更喜欢将列大小与预期内容相关。例如,即使包含国家/地区代码和分机号,电话号码也不会超过 50 个字符。同样,邮政编码很可能不超过 20 个字符。