#1 楼
在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。在这种情况下,我的钱仍将放在CHAR上,而不是VARCHAR上。CHAR与VARCHAR的问题只有通过适当的计划才能解决。强大的能力带来了巨大的责任(陈词滥调,但确实如此)。
UPDATE
当涉及到MD5时,在切换整个行格式时应在内部消除
strlen
的计算。无需更改字段定义。如果MD5键是唯一存在的VARCHAR,我将使用它并将表行格式转换为固定格式。如果存在大量其他VARCHAR字段,它们也将受益。作为交换,桌子将扩大到其大小的两倍左右。但是,如果不进行其他调整,查询的速度应提高20%左右。
评论
我想我会使用char(4)或类似无符号整数的IP地址
–杰克·道格拉斯(Jack Douglas)
2011年5月10日21:28
@JackPDouglas在这一点上你是正确的。
– RolandoMySQLDBA
2011年5月10日21:36
索引不是以固定长度存储吗?我不知道如何将存储格式更改为固定长度的改进的索引查找。您是说它改善了表扫描吗?
–马库斯·亚当斯(Marcus Adams)
2012年4月15日在22:24
@JackDouglas,为什么不位和二进制?
–起搏器
2014年12月10日,下午3:46
@Pacerier会更好,我同意:)
–杰克·道格拉斯(Jack Douglas)
2014年12月10日下午5:32
#2 楼
看起来您可以通过转换为char
来为每个值节省1个字节或大约3%。如果仍然以十六进制存储MD5,则可能不值得-而是使用binary
可以节省50%。感谢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;
| length(bar) | | ----------: | | 32 | | 16 |
db <>在这里拨弄
评论
改用二进制文件的好方法。
–RThomas
2011年5月10日在21:17
我打算将其转换为二进制文件。现在,我考虑了一下,因为我们的编码是utf-8,所以大小不应仅基于我使用的是字节还是字符而有所不同。还是我错了?
–杰森·贝克(Jason Baker)
2011年5月17日17:36
@Jason-编码不适用于二进制-还是我误解了?
–杰克·道格拉斯(Jack Douglas)
2011年5月17日在18:53
对于字符集为utf-8的char(32)列,每个值都需要32x3字节进行存储。为什么需要将MD5哈希值设置为utf-8。转换为binary(32)将需要每个值32个字节。
–ovais.tariq
11年5月25日在12:05
除非您也使用UNHEX(),否则更改为BINARY几乎没有作用。也就是说,您可以将UNHEX(MD5(x))存储到16字节的BINARY(16)中,以节省将MD5(x)存储到CHAR(32)CHARACTER SET ascii中的空间。
–里克·詹姆斯(Rick James)
18年8月16日在5:11
#3 楼
我认为这不值得改变。如果您浏览此处的文档,则应说明两者之间的区别。在您的使用场景中,除非您真的担心与行大小相关的额外开销,否则一个并不会真正提供任何明显的好处。/doc/refman/5.0/zh-CN/char.html
还请注意上面链接到的文档的第一条评论...“如果整个记录都是固定大小的,则CHAR只会加快访问速度也就是说,如果使用任何可变大小的对象,也可以使它们全部变为可变大小。通过在还包含VARCHAR的表中使用CHAR不会加快速度。
评论
该“加速”适用于MyISAM,而不适用于InnoDB。
–里克·詹姆斯(Rick James)
16年7月2日在5:17
评论
警告此问题及其答案是在InnoDB和utf8为默认值之前编写的。