MySQL CHAR和VARCHAR存储、读取时的差别

 更新时间:2020年11月05日 10:15:05   作者:Lenix  
这篇文章主要介绍了MySQL CHAR和VARCHAR存储的差别,帮助大家更好的理解和使用MySQL数据库,感兴趣的朋友可以了解下

导读

你真的知道CHAR和VARCHAR类型在存储和读取时的区别吗?

还是先抛几条结论吧:

1、存储的时候,CHAR总是会补足空格后再存储,不管用户插入数据时尾部有没有包含空格。

2、存储的时候,VARCHAR不会先补足空格后再存储,但如果是用户在插入时特地加了空格那就会如实存储,而不会给删除。

3、读取数据时,CHAR总是会删除尾部空格(哪怕是写入时包含空格)。

4、读取数据时,VARCHAR总是如实取出之前存入的值(如果存储时尾部包含空格,就会继续保留着,不会像CHAR那样删除尾部空格)。

下面是测试验证过程。

1、测试CHAR类型

表结构:

CREATE TABLE `tchar` (
 `id` int(10) unsigned NOT NULL DEFAULT '0',
 `c1` char(20) NOT NULL DEFAULT '',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入几条记录:

insert into tchar values (1, concat('a', repeat(' ',19)));
insert into tchar values (2, concat(' ', repeat('a',19)));
insert into tchar values (3, 'a');
insert into tchar values (4, ' ');
insert into tchar values (5, '');

查看存储结构:

(1) INFIMUM record offset:99 heapno:0 ...
(2) SUPREMUM record offset:112 heapno:1 ...
(3) normal record offset:126 heapno:2 ... <- id=1
(4) normal record offset:169 heapno:3 ... <- id=2
(5) normal record offset:212 heapno:4 ... <- id=3
(6) normal record offset:255 heapno:5 ... <- id=4
(7) normal record offset:298 heapno:6 ... <- id=5

看到这坨东西有点懵是不是,还记得我给你们安利过的一个工具不,看这里:innblock | InnoDB page观察利器。

可以看到,无论我们存储多长的字符串进去,每条记录实际都是占用43(169-126=43)字节。由此结论1成立。
简单说下,43字节的由来:
DB_TRX_ID, 6字节。
DB_ROLL_PTR, 7字节。
id, int, 4字节。
c1, char(20), 20字节;因为是CHAR类型,还需要额外1字节。
每条记录总是需要额外5字节头信息(row header)。
这样总的加起来就是43字节了。

再看下读取tchar表的结果:

select id,concat('000',c1,'$$$'),length(c1) from tchar ;
+----+----------------------------+------------+
| id | concat('000',c1,'$$$')  | length(c1) |
+----+----------------------------+------------+
| 1 | 000a$$$     |   1 | <- 删除尾部空格
| 2 | 000 aaaaaaaaaaaaaaaaaaa$$$ |   20 |
| 3 | 000a$$$     |   1 |
| 4 | 000$$$      |   0 | <- 删除尾部空格,结果和id=5一样
| 5 | 000$$$      |   0 |
+----+----------------------------+------------+

2、测试VARCHAR类型

表结构:

CREATE TABLE `tvarchar` (
 `id` int(10) unsigned NOT NULL DEFAULT '0',
 `c1` varchar(20) NOT NULL DEFAULT '',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

插入几条记录:

insert into tvarchar values (1, concat('a', repeat(' ',19)));
insert into tvarchar values (2, concat(' ', repeat('a',19)));
insert into tvarchar values (3, 'a');
insert into tvarchar values (4, ' ');
insert into tvarchar values (5, '');
insert into tvarchar values (6, '');

查看存储结构:

(1) INFIMUM record offset:99 heapno:0 ...
(2) SUPREMUM record offset:112 heapno:1 ...
(3) normal record offset:126 heapno:2 ... <- id=1
(4) normal record offset:169 heapno:3 ... <- id=2
(5) normal record offset:212 heapno:4 ... <- id=3
(6) normal record offset:236 heapno:5 ... <- id=4
(7) normal record offset:260 heapno:6 ... <- id=5
(8) normal record offset:283 heapno: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表的结果:

select id,concat('000',c1,'$$$'),length(c1) from tvarchar;
+----+----------------------------+------------+
| id | concat('000',c1,'$$$')  | length(c1) |
+----+----------------------------+------------+
| 1 | 000a     $$$ |   20 | <- 读取结果中没有删除尾部的空格
| 2 | 000 aaaaaaaaaaaaaaaaaaa$$$ |   20 |
| 3 | 000a$$$     |   1 |
| 4 | 000 $$$     |   1 | <- 读取结果中没有删除此空格
| 5 | 000$$$      |   0 |
| 6 | 000$$$      |   0 |
+----+----------------------------+------------+

总的来说,可以总结成两条结论:
1、从读取的结果来看,CHAR类型列看起来像是在存储时把空格给吃了,但实际上只是在读取时才给吃了(显示层面上把空格删除了)。
2、从读取的结果来看,VARCHAR类型列看起来像是反倒保留了多余的空格,实际上也是只在读取时才恢复这些空格(但实际物理存储时还是会删掉这些空格)。

最后,来看下文档里怎么说的:

When CHAR values are stored, they are right-padded with spaces to the
specified length. 简言之,CHAR列在存储时尾部加空格补齐长度。

When CHAR values are retrieved, trailing spaces are removed unless the
PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.
简言之,CHAR列在读取时会去掉尾部空格,除非设置sql_mode值PAD_CHAR_TO_FULL_LENGTH=1。

VARCHAR values are not padded when they are stored.
简言之,存VARCHAR时尾部不加空格。

Trailing spaces are retained when values are stored and retrieved, in
conformance with standard SQL. 简言之,读取VARCHAR时会显示空格。

以上测试使用的版本及环境:

mysql> select version()\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.1 The CHAR and VARCHAR Types,https://dev.mysql.com/doc/refman/5.7/en/char.html

以上就是MySQL CHAR和VARCHAR存储的差别的详细内容,更多关于MySQL CHAR和VARCHAR的资料请关注脚本之家其它相关文章!

相关文章

  • MySQL8.0.30(zip版)windows安装过程

    MySQL8.0.30(zip版)windows安装过程

    这篇文章主要介绍了MySQL8.0.30(zip版)windows安装,进入到mysql下载页面后选择zip版本点击下载,下载完以后把它解压到自己想存放的位置,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • MySQL锁的知识点总结

    MySQL锁的知识点总结

    在本篇文章里小编给大家整理了关于MySQL锁的知识点总结以及实例内容,需要的朋友们学习下。
    2019-11-11
  • mysql 5.7.17 以及workbench安装配置图文教程

    mysql 5.7.17 以及workbench安装配置图文教程

    这篇文章主要为大家详细介绍了mysql 5.7.17 以及workbench安装配置图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • 详解MySQL中SlowLog的配置方法(图文)

    详解MySQL中SlowLog的配置方法(图文)

    mysql 日志系统上线有段时间了,前端在慢慢切站点过来写入,未雨绸缪 diy了套 mysql 监控工具
    2014-02-02
  • 解决mysql8.0.19  winx64版本的安装问题

    解决mysql8.0.19 winx64版本的安装问题

    这篇文章主要介绍了mysql8.0.19 数据库 winx64版本的安装问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • MySQL 5.7安装后默认登录密码的查看方法

    MySQL 5.7安装后默认登录密码的查看方法

    许多朋友安装MySQL 5.7之后不知道默认登录密码在哪里查看,本文小编将通过代码示例和图文结合的方式给大家介绍MySQL 5.7安装后默认登录密码的查看方法,需要的朋友可以参考下
    2023-12-12
  • 深入SQLite基本操作的总结详解

    深入SQLite基本操作的总结详解

    本篇文章是对SQLite的基本操作进行了总结和介绍。需要的朋友参考下
    2013-05-05
  • 基于MySQL和Redis扣减库存的实践

    基于MySQL和Redis扣减库存的实践

    本文主要介绍了基于MySQL和Redis扣减库存的实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • MySQL中sum函数使用的实例教程

    MySQL中sum函数使用的实例教程

    这篇文章主要给大家介绍了关于MySQL中sum函数使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 通过代码实例了解页面置换算法原理

    通过代码实例了解页面置换算法原理

    这篇文章主要介绍了通过代码实例了解页面置换算法原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08

最新评论