在 MySQL 中,char 和 varchar 可能是我们最常使用字符串类型。那么到底 varchar 和 varchar 有什么不同?我们什么时候使用 char,什么时候使用 varchar 呢?
区别
char
char 是定长的,插入数据不足规定长度的,右边补空格,当然查询出来的数据也会有空格,插入数据超过规定长度,会返回错误[22001][1406] Data truncation: Data too long for column 'name' at row 1
,MySQL
并不会自动截短字符串。因为 char 是定长的,所以查询的效率比 varchar 高(后面会将为什么效率高),但在列容量不能充分利用的情况下会造成一定的空间浪费。
varchar
varchar 是不定长的,varchar 类型的列是不定长的,在 5.0 版本以后的最大长度是 65536 字节(2^16), 但是这个长度只是 “系统长度”,这并不意味着你真的可以完全利用 65536 字节来存储数据,因为 varchar 是不定长的,所以需要前两个字节标记字段的实际长度,结尾还要用一个字节表示结束。
需要注意的是 65535 只是字节个数,而且是理论字节个数,在减去头尾的 “系统” 占用字节后,只剩下 65533 可用字节。那么我们建表的时候,能不能直接写 varchar(65533) 呢?当然是不可以的,因为 4.0 之后,varchar 后面的小括号里就不再是字节长度了,而是字符长度。 字节和字符个数之间的换算关系是根据编码决定的:
编码 | 长度 |
---|---|
utf8 | 65533/3=21844(汉字占 3 个字符) |
utf8mb4 | 65533/4=16383(汉字占 4 个字符,包含了生僻汉字和文字表情) |
我们只列出了常用的编码格式。
那么这是否意味着,在 utf8mb4 编码下我们可以用 varchar(16383) 来定义一个列呢?
答案是要看情况,MySQL 规定了一个row
所有的字段加起来总长度不能超过 65535 字节,所以如果一个表只有一个列,那完全可以用 varchar(16383) 来定义这个列,如果这个表还有其他列,无论其他列多么短,都是会占用字节数的,所以,使用 varchar(16383) 来定义的时候,MySQL
会返回错误提示:ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
, 意思是 row 的容量太大,超出了 row 的最大容量 65535,如果不改变列的长度的话,推荐使用TEXT or BLOBs
类型。
所以,如果我们要创建一个只包含两个字段的表(编码是 utf8mb4),一列是主键,一列是字符串,字符串的最大长度是多少呢?你可以先自己算一下,再往下看。
列 | 长度 |
---|---|
id | int(11) |
article | varchar((65535-4)/4=16382) |
为什么 65535 要减去 4 呢?因为 int(11) 占 4 个字节,那么在 utf8 编码情况下,还是同样的数据结构,article 的最大长度有事多少呢?
列 | 长度 |
---|---|
id | int(11) |
article | varchar((65535-4)/3=21843) |
相信这次你一定算对了。
为什么 char 类型查询效率高
这是由他们在磁盘上存放的不同形式决定的,我们先来看一个图:
char 和 varchar 类型数据存储示意图. jpg
我们可以看到 char 类型在存放数据的时候,中间是没有间隔的,数据本身是有空格的,但是数据段之间没有间隔,因为我们在创建列的时候已经告诉MySQL
列的长度了,MySQL
在查询数据的时候,只需要按部就班寻找就行了,不需要在中途计算这个数据段的长度。
但是 varchar 类型的存放就不同了,在每个数据段开头,都要有一段空间(1~2 个字节)存放数据段的长度,在数据段的结尾还有一段空间(1 个字节)标记此字段的节数。MySQL
在读取一个数据段的时候,首先要读开头,比如读到了 3,说明数据段的长度是 3,之后就不多不少,只读 3 个字节。所以MySQL
在遍历数据的时候,磁针要比 char 类型的列多读很多次磁盘来获取字段的真实长度,这就是为什么 varchar 比 char 查询效率低的原因了。
应用
我们可以用 varchar 存放不定长的数据,比如人的名字,或者一篇博客的文章。可以用 char 存放定长的数据,比如身份证号和手机号,我们把一个列定义为mobile varchar(11)
,中国大陆的手机号最长,达到 11 位,香港是 8 位,瑞士是 10 位,所以定义成 11 位完全够用,可以存放各国的手机号了。
附加
除了 char 和 varchar 类型,最常用的就是数值类型了,为了方便建表的时候计算列的最大长度,把数值类型占用的字节和值的范围放在这里:
MySQL 数值类型的取值范围和占用字节数. png