MySQL CHAR和VARCHAR存储、读取时的差别
导读
你真的知道CHAR和VARCHAR类型在存储和读取时的区别吗?
还是先抛几条结论吧:
1、存储的时候,CHAR总是会补足空格后再存储,不管用户插入数据时尾部有没有包含空格。
2、存储的时候,VARCHAR不会先补足空格后再存储,但如果是用户在插入时特地加了空格那就会如实存储,而不会给删除。
3、读取数据时,CHAR总是会删除尾部空格(哪怕是写入时包含空格)。
4、读取数据时,VARCHAR总是如实取出之前存入的值(如果存储时尾部包含空格,就会继续保留着,不会像CHAR那样删除尾部空格)。
下面是测试验证过程。
1、测试CHAR类型
表结构:
CREATETABLE`tchar`( `id`int(10)unsignedNOTNULLDEFAULT'0', `c1`char(20)NOTNULLDEFAULT'', PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;
插入几条记录:
insertintotcharvalues(1,concat('a',repeat('',19))); insertintotcharvalues(2,concat('',repeat('a',19))); insertintotcharvalues(3,'a'); insertintotcharvalues(4,''); insertintotcharvalues(5,'');
查看存储结构:
(1)INFIMUMrecordoffset:99heapno:0... (2)SUPREMUMrecordoffset:112heapno:1... (3)normalrecordoffset:126heapno:2...<-id=1 (4)normalrecordoffset:169heapno:3...<-id=2 (5)normalrecordoffset:212heapno:4...<-id=3 (6)normalrecordoffset:255heapno:5...<-id=4 (7)normalrecordoffset:298heapno:6...<-id=5
看到这坨东西有点懵是不是,还记得我给你们安利过的一个工具不,看这里:innblock|InnoDBpage观察利器。
可以看到,无论我们存储多长的字符串进去,每条记录实际都是占用43(169-126=43)字节。由此结论1成立。
简单说下,43字节的由来:
DB_TRX_ID,6字节。
DB_ROLL_PTR,7字节。
id,int,4字节。
c1,char(20),20字节;因为是CHAR类型,还需要额外1字节。
每条记录总是需要额外5字节头信息(rowheader)。
这样总的加起来就是43字节了。
再看下读取tchar表的结果:
selectid,concat('000',c1,'$$$'),length(c1)fromtchar; +----+----------------------------+------------+ |id|concat('000',c1,'$$$')|length(c1)| +----+----------------------------+------------+ |1|000a$$$|1|<-删除尾部空格 |2|000aaaaaaaaaaaaaaaaaaa$$$|20| |3|000a$$$|1| |4|000$$$|0|<-删除尾部空格,结果和id=5一样 |5|000$$$|0| +----+----------------------------+------------+
2、测试VARCHAR类型
表结构:
CREATETABLE`tvarchar`( `id`int(10)unsignedNOTNULLDEFAULT'0', `c1`varchar(20)NOTNULLDEFAULT'', PRIMARYKEY(`id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4
插入几条记录:
insertintotvarcharvalues(1,concat('a',repeat('',19))); insertintotvarcharvalues(2,concat('',repeat('a',19))); insertintotvarcharvalues(3,'a'); insertintotvarcharvalues(4,''); insertintotvarcharvalues(5,''); insertintotvarcharvalues(6,'');
查看存储结构:
(1)INFIMUMrecordoffset:99heapno:0... (2)SUPREMUMrecordoffset:112heapno:1... (3)normalrecordoffset:126heapno:2...<-id=1 (4)normalrecordoffset:169heapno:3...<-id=2 (5)normalrecordoffset:212heapno:4...<-id=3 (6)normalrecordoffset:236heapno:5...<-id=4 (7)normalrecordoffset:260heapno:6...<-id=5 (8)normalrecordoffset:283heapno:7...<-id=6
可以看到,几条记录的字节数分别是:43、43、24、24、23、23(最后一条记录和id=5那条记录一样)。
对上面这个结果有点诧异是不是,尤其是id=1的记录(插入的是'a…后面19个空格'),居然也要消耗43字节,这就佐证了上面的结论2。
同样的,id=3和id=4这两条记录都是占用24字节,而id=5和id=6这两条记录都是占用23字节(没有额外存储字符串的字节数,只有id列4个字节)。
再看下读取tvarchar表的结果:
selectid,concat('000',c1,'$$$'),length(c1)fromtvarchar; +----+----------------------------+------------+ |id|concat('000',c1,'$$$')|length(c1)| +----+----------------------------+------------+ |1|000a$$$|20|<-读取结果中没有删除尾部的空格 |2|000aaaaaaaaaaaaaaaaaaa$$$|20| |3|000a$$$|1| |4|000$$$|1|<-读取结果中没有删除此空格 |5|000$$$|0| |6|000$$$|0| +----+----------------------------+------------+
总的来说,可以总结成两条结论:
1、从读取的结果来看,CHAR类型列看起来像是在存储时把空格给吃了,但实际上只是在读取时才给吃了(显示层面上把空格删除了)。
2、从读取的结果来看,VARCHAR类型列看起来像是反倒保留了多余的空格,实际上也是只在读取时才恢复这些空格(但实际物理存储时还是会删掉这些空格)。
最后,来看下文档里怎么说的:
WhenCHARvaluesarestored,theyareright-paddedwithspacestothe
specifiedlength.简言之,CHAR列在存储时尾部加空格补齐长度。WhenCHARvaluesareretrieved,trailingspacesareremovedunlessthe
PAD_CHAR_TO_FULL_LENGTHSQLmodeisenabled.
简言之,CHAR列在读取时会去掉尾部空格,除非设置sql_mode值PAD_CHAR_TO_FULL_LENGTH=1。VARCHARvaluesarenotpaddedwhentheyarestored.
简言之,存VARCHAR时尾部不加空格。Trailingspacesareretainedwhenvaluesarestoredandretrieved,in
conformancewithstandardSQL.简言之,读取VARCHAR时会显示空格。
以上测试使用的版本及环境:
mysql>selectversion()\G ... version():8.0.15 mysql>select@@sql_mode\G ... @@sql_mode:ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
参考文档
11.4.1TheCHARandVARCHARTypes,https://dev.mysql.com/doc/refman/5.7/en/char.html
以上就是MySQLCHAR和VARCHAR存储的差别的详细内容,更多关于MySQLCHAR和VARCHAR的资料请关注毛票票其它相关文章!