在固定大小的字段上使用CHAR vs VARCHAR会对性能产生什么影响?


58

我有一个存储MD5哈希的索引列。因此,该列将始终存储32个字符的值。无论出于何种原因,它都是作为varchar而不是char创建的。迁移数据库以将其转换为char值得麻烦吗?这是在带有InnoDB的MySQL 5.0中。


6
警告此问题及其答案是在InnoDB和utf8为默认值之前编写的。
瑞克·詹姆斯

Answers:


56

之前有人问过类似的问题

MySQL VARCHAR大小的性能影响

这是我答案的摘录

您必须意识到使用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数据库设计和调优》这本书在MyISAM表上做了出色的工作来证明这一点。本书中的示例执行了以下操作:

ALTER TABLE tblname ROW_FORMAT=FIXED;

该指令强制所有VARCHAR充当CHAR。我在2007年的上一份工作中做到了这一点,并使用了300GB的表,并将索引查找速度提高了20%,而没有进行任何其他更改。它像出版一样工作。但是,它的确产生了几乎两倍大的表,但这只是权衡第一。

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

SELECT * FROM tblname PROCEDURE ANALYSE();

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

如果要存储IP地址,则该列的掩码最多为15个字符(xxx.xxx.xxx.xxx)。我会CHAR(15)心跳一跳,因为IP地址的长度不会有太大变化,而且字符串处理的复杂性由一个额外的字节控制。您仍然可以PROCEDURE ANALYSE()针对这样的列执行操作。它甚至可能建议使用VARCHAR。在这种情况下,我的钱仍将超过VARCHAR放在CHAR上。

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

更新

说到MD5,strlen在切换整个行格式时,应消除内部计算。无需更改字段定义。

如果MD5键是唯一存在的VARCHAR,我将使用它并将表行格式转换为fixed。如果存在大量其他VARCHAR字段,它们也将受益。作为交换,桌子将扩展到其大小的两倍左右。但是,在不进行其他调整的情况下,查询应该可以加快20%的速度。


1
我想我会使用char(4)或类似无符号整数的IP地址
Jack Douglas

@JackPDouglas在那一点上你是正确的。
RolandoMySQLDBA 2011年

索引不是以固定长度存储吗?我不知道如何将存储格式更改为固定长度的改进索引查找。您是说它改善了表扫描吗?
马库斯·亚当斯

1
@JackDouglas,为什么不bitbinary
Pacerier 2014年

@Pacerier会更好,我同意:)
Jack Douglas

19

看来您将每个值转换为可以节省1个字节或大约3%char。如果您仍然以十六进制存储MD5,则可能不值得-通过使用a可以节省50%binary

感谢Ovais(请参阅注释)指出,如果使用多字节字符集,则char(32)可以使用32个以上的字节。

感谢Rick James指出您应该使用unhex函数将十六进制字符串转换为二进制:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| 长度(巴)|
| ----------:|
| 32 |
| 16 |

db <> 在这里拨弄


改用二进制文件好了。
RThomas 2011年

我打算将其转换为二进制文件。现在,我考虑了一下,由于我们的编码是utf-8,因此大小不应仅基于我使用的是字节还是字符而有所不同。还是我错了?
杰森·贝克

@Jason-编码不适用于binary-还是我误解了?
杰克·道格拉斯

3
对于字符集为utf-8的char(32)列,每个值将需要32x3字节进行存储。为什么需要将MD5哈希值设置为utf-8。转换为binary(32)将需要每个值32个字节。
ovais.tariq

1
BINARY除非也使用,否则更改为几乎没有作用UNHEX()。也就是说,你可以储存UNHEX(MD5(x))到一个16字节的BINARY(16)过度节省存储空间显著MD5(x)CHAR(32) CHARACTER SET ascii
瑞克·詹姆斯

15

我认为这不值得改变。如果您浏览此处的文档,则应说明两者之间的区别。在您的使用场景中,除非您真的担心与行大小相关的额外开销,否则一个并不会真正提供任何明显的好处。

http://dev.mysql.com/doc/refman/5.0/en/char.html

还要注意上面我链接到的文档的第一条评论...“只有在整个记录都是固定大小的情况下,CHAR才会加快访问速度。也就是说,如果使用任何可变大小的对象,都可以将它们全部制成可变大小。通过在还包含VARCHAR的表中使用CHAR,您不会获得任何速度”


该“加速”适用于MyISAM,不适用于InnoDB。
瑞克·詹姆斯
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.