我有一个存储 MD5 哈希的索引列。因此,该列将始终存储 32 个字符的值。无论出于何种原因,它被创建为 varchar 而不是 char。迁移数据库以将其转换为字符是否值得麻烦?这是在带有 InnoDB 的 MySQL 5.0 中。
您必须意识到使用 CHAR 与 VARCHAR 的权衡
使用 CHAR 字段,您分配的正是您得到的。例如,CHAR(15) 分配并存储 15 个字节,无论您在字段中放置何种字符。字符串操作简单直接,因为数据字段的大小是完全可预测的。
使用 VARCHAR 字段,您会得到一个完全不同的故事。例如,VARCHAR(15) 实际上动态分配最多 16 个字节,最多 15 个用于数据,至少还有 1 个额外的字节来存储数据的长度。如果您要存储字符串“hello”,则需要 6 个字节,而不是 5 个字节。字符串操作在所有情况下都必须始终执行某种形式的长度检查。
当您做两件事时,这种权衡更加明显: 1. 存储数百万或数十亿行 2. 索引 CHAR 或 VARCHAR 列
TRADEOFF #1 显然,VARCHAR 具有优势,因为可变长度数据会产生更小的行,从而产生更小的物理文件。
TRADEOFF #2 由于 CHAR 字段由于字段宽度固定而需要较少的字符串操作,因此针对 CHAR 字段的索引查找平均比 VARCHAR 字段快 20%。这不是我的任何猜测。MySQL Database Design and Tuning 一书在 MyISAM 表上做了一些奇妙的事情来证明这一点。书中的例子做了如下的事情:
ALTER TABLE tblname ROW_FORMAT=FIXED;
该指令强制所有 VARCHAR 行为为 CHAR。早在 2007 年,我在上一份工作中就做到了这一点,并使用了一个 300GB 的表并将索引查找速度提高了 20%,而没有更改任何其他内容。它按已发布的方式工作。然而,它确实产生了一个几乎两倍大小的表,但这又回到了权衡#1。
您可以分析存储的数据以查看 MySQL 推荐的列定义。只需对任何表运行以下命令:
SELECT * FROM tblname PROCEDURE ANALYSE();
这将遍历整个表,并根据其包含的数据、最小字段值、最大字段值等为每一列推荐列定义。有时,您只需要使用常识来规划 CHAR 与 VARCHAR。这是一个很好的例子:
如果要存储 IP 地址,则此类列的掩码最多为 15 个字符 (xxx.xxx.xxx.xxx)。我会立刻跳起来,CHAR(15)因为 IP 地址的长度不会有太大的变化,而且字符串操作的复杂性由额外的字节控制。你仍然可以PROCEDURE ANALYSE()反对这样的专栏。它甚至可能会推荐 VARCHAR。在这种情况下,我的钱仍然会放在 CHAR 上而不是 VARCHAR 上。
CHAR(15)
PROCEDURE ANALYSE()
CHAR 与 VARCHAR 问题只能通过适当的计划来解决。强大的力量伴随着巨大的责任(陈词滥调但真实)。
更新
说到MD5,strlen在切换整行格式的时候,内部的计算应该去掉。无需更改字段定义。
strlen
如果 MD5 键是唯一存在的 VARCHAR,我会选择它并将表行格式转换为 fixed。如果存在大量其他 VARCHAR 字段,它们也会受益。作为交换,桌子将扩大到大约两倍的大小。但是查询应该在没有额外调整的情况下加速大约 20%。