我们都知道,MySQL中关于字符,有char和varchar两种常用的类型,可能在平时的使用过程中,大家不会去关心这两种类型的区别,只是会用就可以了,或者说看到过一些它们的区别,但是没有时间去测试,今天有时间了,我将这两种类型的具体情况实验一把,让大家直观感受下,纯属分享,大神请绕道。
先说说理论吧。
char类型为固定长度的字符串,比如说char(10),它定义了指定的字符串长度最大为10个字符,如果你现在输入一个字符串为'12345678',那么它在char类型中到底会占用多少个字符呢?答案是10个,后面缺少的2个字符,MySQL会自动补充为空值,然后进行存放。在取这个记录的时候,char类型的会使用trim()函数去掉多余的空格,所以我们看到的还是8个字符的记录。当输入的字符长度大于最大的长度时,MySQL会自动报错。
varchar类型是长度可变的字符串,varchar(M)表示最大长度是M个字符,varchar的最大实际长度由最大的行的大小和使用的字符集确定。例如varchar(50)定义了一个最大长度为50的字符串,如果插入的字符串只有20个字符,那么实际存储的字符串具有21个字符,因为varchar会自动包含一个字符串结束字符。varchar在值保存和检索时,尾部的空格仍然保留。
介绍完概念,我们来看具体的实践过程,本文中使用的测试版本为MySQL5.7.22版本。
1.测试char的trim()功能
首先创建一个表,这个表里面包含两个字段,d_char和d_varchar,设定初始的字符长度都为4,如下:
查看一下,
此时,我们插入两条记录,每条记录都是'ab ',注意,ab后面有2个空格,
然后我们使用mysql里面的concat函数进行字符连接,给每条记录的左右分别添加小括号,
此时我们可以看到,d_char的ab后面的空格被取消掉了,而d_varchar后面的空格还依旧存在。
2.测试两种字符类型的最大长度
首先看看char的最大长度,我们设置的值为256,结果如下
所以,char类型的长度取值范围为0~255个字符
上面提到了varchar的最大实际长度由最大的行的大小和使用的字符集确定,这里我们进行实验:
可以看到,字符集不一样,最后的max的值也是不一样的,
utf8模式下是0~21845,一个字符占三个字节,最多能存 21844 个字符
latin1模式下是0~65535,一个字符占一个字节,最多能存放 65532 个字符
gbk模式下是0~32767,一个字符占两个字节,最多能存 32766 个字符
若定义的时候超过上述限制,则varchar字段会被强行转为text类型,并产生warning。
可能这里有人要问了,为什么最大值是32767,而最多只能放32766个字符呢?
举两个例说明一下实际长度的计算。
a) 若一个表只有一个varchar类型,如定义为
create table t4(c varchar(N)) charset=gbk;
则此处N的最大值为(65535-1-2)/2= 32766 个字符。
减1的原因是实际行存储从第二个字节开始’;
减2的原因是varchar头部的2个字节表示长度;
除2的原因是字符编码是gbk。
b) 若一个表定义为
create table t4(c int, c2 char(30), c3 varchar(N)) charset=utf8;
则此处N的最大值为 (65535-1-2-4-30*3)/3=21812
减1和减2与上例相同;
减4的原因是int类型的c占4个字节;
减30*3的原因是char(30)占用90个字节,编码是utf8。
如果被varchar超过上述的b规则,被强转成text类型,则每个字段占用定义长度为11字节,当然这已经不是“varchar”了。
则此处N的最大值为 (65535-1-2-4-30*3)/3=21812
3.MySQL的字段长度模式
字段长度的模式分为严格模式和不严格模式,在严格模式下,如果我们想给一个字段中插入一个大于规定长度的字符串,MySQL会给出错误提示,例如我们的表:
当我们插入一个大于4字符的记录时,
如果在非严格模式下,mysql会自动截断超出最大长度的字符,
上面的操作是,我们先把字段模式改为非严格模式,然后查询更改,确保更改生效,接着我们插入'abcde'字符串,发现它可以被成功执行,但是包含两个警告,查看警告可以发现,一些数据被截断了,
实验部分的内容基本就完成了,这里我们进行几点分析:
1.MySQL为什么要设置这两种类型?它们各自有什么优点?
char是固定长度的,它的存取速度比varchar快,方便程序的存储于查找,但是它需要浪费一定的空间,可以看做是一种以空间换时间的方法。
而varchar的特点是可变长,当定义一个varchar(10)而只存入了4个字符,此时varchar会直接将字符记录的长度变为4,从而节省空间,它可以看做是一种用时间换取空间的方法。
char的存储方式是,对英文字符(ASCII)占用1个字节,对一个汉字占用两个字节;而varchar的存储方式是,对每个英文字符占用2个字节,汉字也占用2个字节,两者的存储数据都非unicode的字符数据。
2.两种类型适应的情况分析。
关于char:
CHAR适合存储很短的字符串,或者所有值都接近同一个长度。
对于经常变更的数据,CHAR也比VARCHAR更好,因为定长的CHAR类型不容易产生碎片。
对于非常短的列,CHAR在存储空间上也更有效率。例如用char(1)来存储只有Y和N的值,只需要一个字节,但是varchar却需要两个字节,因为还一个记录长度的额外字节。
关于varchar
VARCHAR类型用于存储可变长字符串,是最常见的字符串数据类型。它比定长类型 更节省空间,因为它仅使用必要的空间(例如,越短的字符串使用越少的空间)。
VARCHAR节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要做额外的工作。如果一个行占用 的空间增长,并且在页内没有更多的空间可以存储,在这种情况下,不同的存储引擎的处理方式是不一样的。例如,MyISAM会将行拆成不同的片段存储,InnoDB 则需要分裂页来使行可以放进页内。
VARCHAR需要使用1或2个额外字节记录字符串的长度:如果列的最大长度小于或等于255字节,则只使用1个字节表示,否则使用2个字节。假设采用latinl字符集 ,一个varchar(10)的列需要11个字节的存储空间。varchar(1000)的列则需要1002个字节,因为需要2个字节存储长度信息。
适用情况:
1、对于MyISAM表,尽量使用Char,对于那些经常需要修改而容易形成碎片的myisam和isam数据表就更是如此,它的缺点就是占用磁盘空间;
2、对于InnoDB表,因为它的数据行内部存储格式对固定长度的数据行和可变长度的数据行不加区分(所有数据行共用一个表头部分,这个标头部分存放着指向各有关数据列的指针),所以使用char类型不见得会比使用varchar类型好。事实上,因为char类型通常要比varchar类型占用更多的空间,所以从减少空间占用量和减少磁盘i/o的角度,使用varchar类型反而更有利;
3、存储很短的信息,比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个byte用于存储信息长度,本来打算节约存储的现在得不偿失。
4、固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据长度的特性就消失了,而且还要占个长度信息。
5、十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。
关于MySQL之char、varchar,你学废了么?