点击上方“Java基基”,选择“设为星标”

做积极的人,而不是积极废人!

每天 14:00 更新文章,每天掉亿点点头发...

源码精品专栏

  • 原创 | Java 2021 超神之路,很肝~

  • 中文详细注释的开源项目

  • RPC 框架 Dubbo 源码解析

  • 网络应用框架 Netty 源码解析

  • 消息中间件 RocketMQ 源码解析

  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析

  • 作业调度中间件 Elastic-Job 源码解析

  • 分布式事务中间件 TCC-Transaction 源码解析

  • Eureka 和 Hystrix 源码解析

  • Java 并发源码

来源:learn.blog.csdn.net/

article/details/103341778

  • 文章目录

  • VARCHAR的定义

  • VARCHAR的最大长度

    • 最大行大小

    • 可空列标识位

    • 字符集的单字符最大字节数

    • VARCHAR的长度标识位

    • 样例


以我多年经验来看,VARCHAR的最大长度、字符串类型选择,用MySQL的人中十之七八是不清楚的。网上文章鱼目混珠,以讹传讹居多。

本文不止介绍了原理,还提供了案例手把手教你自己分析,彻底解决你的疑惑。

假设有个VARCHAR(64) CHARSET utf8mb4列,存储了中国cn这个字符串。

那你猜一猜,MySQL存储时用了多少字节?

  • A:4 Bytes

  • B:5 Bytes

  • C:8 Bytes

  • D:9 Bytes

  • E:10 Bytes

  • F:10.125 Bytes

  • G:11 Bytes

  • H:12 Bytes

  • I:12.125 Bytes

  • K:13 Bytes

正确答案是F和G。

如果您没猜对,那么花7~10分钟读完本文,即可破解这一谜题。成长快乐轻轻松松。

文章目录

  • VARCHAR的定义

  • VARCHAR的最大长度

    • 最大行大小

    • 可空列标识位

    • 字符集的单字符最大字节数

    • VARCHAR的长度标识位

    • 样例

本文内容适用于MySQL 5.5/5.6/5.7/8.x

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

VARCHAR的定义

VARCHAR是变长字符串。

考虑其变长原理中有较多要素,在具体分解前,有必要一起重温下官方定义。

为了便于理解,我用CHAR定长类型来对比介绍。先看两个小例子:

  • VARCHAR(4),最多存储4个字符,有几个字符存储几个。存储字节数 = 数据值的字节和 + 1字节(长度标识,后面会讲到)

  • CHAR(4),最多存储4个字符,不足4个尾部用空格填满。存储字节数 = 数据值的字节和 + 补位空格数

概括地说,VARCHAR和CHAR都是MySQL的字符串类型,存储多个字符、可设置最大存储的字符数,存储开销都与数据长度、字符集有关。是MySQL最常用的字符串类型。

CHAR和VARCHAR具体对比:

如果开启PAD_CHAR_TO_FULL_LENGTH模式,检索时尾部空格不会去除

CHAR超过255字符会报错,提示使用TEXT或BLOB:

ERROR 1074 (42000): Column length too big for column ''long_char''  (max = 255); use BLOB or TEXT instead

基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。

项目地址:https://github.com/YunaiV/onemall

VARCHAR的最大长度

在MySQL官方定义中,常用的COMPACT、DYNAMIC行模式下,最大长度受几个因素影响:

  • 行存储的最大字节数

  • 数据之外的存储开销,官方定义中包括:NULL标识、长度标识

  • 存储字符的字符集

算法如下:

最大长度(字符数) = (行存储最大字节数 - NULL标识列占用字节数 - 长度标识字节数) / 字符集单字符最大字节数。有余数时向下取整。

下面通过逐步实例验证,演示如何计算出最大长度。

最大行大小

MySQL行默认最大65535字节,是所有列共享的,所以VARCHAR的最大值受此限制。

接下来,我们要创建一个65536字节的VARCHAR,来验证这个边界值。

前面讲过,VARCHAR声明的长度是指字符数。要换算为65536字节,最好一个字符只占一个字节。

所以这里使用了latin1字符集(MySQL默认字符集,不指定即为默认)。

mysql> create table test_varchar_length(v varchar(65536) not null);
ERROR 1074 (42000): Column length too big for column 'v' (max = 65535); use BLOB or TEXT instead

可以看到报错了,提示我们行最大长度为65535字节。

如果我们要插入一个非空的VARCHAR,其最大长度不能超过65535(行最大值) - 2(长度标识位) = 65533字节(长度标识位需2字节才能表示2^16=65536个数字):

/** 测试边界值65534,确认仍然过大;注意这里使用默认字符集latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65534) not null);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs/** 测试边界值65533,创建成功,说明行最大值为65535 */
mysql> create table test_varchar_length(v varchar(65533) not null);
Query OK, 0 rows affected (0.02 sec)/** 查看默认字符集,确认是latin1,每个字符只占用1个字节 */
mysql> show create table test_varchar_length;
+----------------------+------------------------------------------------------------------------------------------------------------+
| Table                | Create Table                                                                                               |
+----------------------+------------------------------------------------------------------------------------------------------------+
| test_varchar_length | CREATE TABLE `test_varchar_length` (`v` varchar(65533) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------------------+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

可空列标识位

在COMPACT、DYNAMIC行格式下,行大小除了数据列长度,还包括可空列标识,即NULL标识位。

如果有一个列允许为空,则需要1 bit来标识,每8 bits的标识会组成一个字段,该字段会存放在每行最开始的位置。

注意,这个标识位不是放在每列,而是每行共享。

假设一张表中存在N个可空字段,NULL标识位需要⌈N / 8 ⌉ (向上取整)个字节。此时整行可用于数据存储的空间只有65535 − ⌈ N / 8 ⌉个字节。

Talk is cheep,一起来验证下:

在行大小的例子中,我们知道最大可创建65533字节长度的非空VARCHAR列。现在要创建一个可空列,每行需要1 bit的NULL标识位、MySQL会将其组装成1 byte的字段存放,那么我们应该可创建最大为65533(最大非空VARCHAR列) - 1(NULL标识列)= 65532字节的可空VARCHAR列:

/** 删除前面创建的表 */
mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)/** 测试边界值65533,确认仍然过大;注意这里使用默认字符集latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65533));
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs/** 测试边界值65532,创建成功,说明可空标识列确实占去了1字节;注意这里使用默认字符集latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65532));
Query OK, 0 rows affected (0.03 sec)

计算VARCHAR的最大长度,可空标识位是最容易忽略的。

字符集的单字符最大字节数

字符集单字符最大字节数不难理解,列举MySQL常见的三个字符集:

  • GBK:单字符最大可占用2个字节。

  • UTF8:单字符最大可占用3个字节。

  • UTF8MB4:单字符最大占4个字节。

假设还有6字节可以存放字符,按单字符占用最大字节数来算,可以存放3个GBK、2个UTF8、1个UTF8MB4。

VARCHAR的长度标识位

长度标识位是相对比较复杂的,网上的介绍错的很多,也容易算错。

其作用是记录数据的字节数。

存储开销是小于255只要1字节、大于255后使用两字节。是因为按照可能的数据大小,分为0 - 255(28)、256 - 65535(216),刚好对应1字节和2字节。

但要注意,其计算根据的是字段声明的字符长度、计算可能的字节数,再决定长度标志的字节数。如VARCHAR(100),字符集为UTF8,可能的字节数为300,长度标识则为2字节。这是网上介绍错的最多的。

另外长度标志位是底层存储开销,不占用字段声明的字符长度。声明的字符长度的是数据的字符数,数据的字节数与字符集有关。

以VARCHAR(1)为例,可以存1个字符,MySQL会额外找一个字节存放长度标识

样例

公式应该都理解了:VARCHAR的最大长度 = (最大行大小 - NULL标识列占用字节数 - 长度标识字节数) / 字符集单字符最大字节数。有余数时向下取整。

接下来通过实验来验证。为了便于理解计算,例子做了一些调整:

  • 不设置可空列、这样可以去掉NULL标识列

  • 为了便于体现长度标识位的差距,采用多个列的形式放大其存在

  • 为了体现按可能字节数计算长度,这里采用多字节的字符集GBK

创建一个表,包含2个非空VARCHAR(127),每个列存储开销为127*2(可能的最大字节数, GBK字符占2字节)+长度标识位1=255字节:

  • 剩余空间为65535 - 255*2 = 65025字节

  • 剩余空间可存放一个VARCHAR(32511) NOT NULL列(32511*2(GBK字符占2字节)+2(长度标识位占2字节)=65024

mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)
/** 测试边界值32512,确认仍然过大 */
mysql> create table test_varchar_length(v1 varchar(127) not null,v2 varchar(127) not null,vm varchar(32512) not null) CHARSET=GBK;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs/** 测试边界值32511,创建成功,说明两个长度标识位共占去了2字节 */
mysql> create table test_varchar_length(v1 varchar(127) not null,v2 varchar(127) not null,vm varchar(32511) not null) CHARSET=GBK;
Query OK, 0 rows affected (0.02 sec)

接下来将两个字段调大到128字符,每个列的存储为最大字节数256+长度标识位2=258字节

  • 剩余空间65535 - 258*2 = 65019字节

  • 剩余空间可存放一个VARCHAR(32508) NOT NULL列(32508*2(GBK字符占2字节)+2(长度标识位占2字节)=65018):

mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)
/** 测试边界值32509,确认仍然过大 */
mysql> create table test_varchar_length(v1 varchar(128) not null,v2 varchar(128) not null,vm varchar(32509) not null) CHARSET=GBK;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs/** 测试边界值32508,创建成功,说明两个长度标识位共占去了4字节 */
mysql> create table test_varchar_length(v1 varchar(128) not null,v2 varchar(128) not null,vm varchar(32508) not null) CHARSET=GBK;
Query OK, 0 rows affected (0.02 sec)

恭喜你,能看到这里的人估计不多,坚持下来的你已经得到了提升。

那么再一起解下最初的问题:

  • UTF8MB4字符中,中文字符需要3个字节(大部分中文只需要3字节,4字节主要是emoji等辅助平面字符),那么“中国cn”需要3+3+1+1共 8个字节

  • VARCHAR(64) CHARSET utf8mb4字段,数据最大可能的字节数是64*4=256,所以需要 2个字节 作为长度标识位;

  • 该字段是可以为空的,那么还需要NULL标识位,MySQL会生成一个 1字节 的NULL标识列来记录;

  • 所以要存储“中国cn”,列需要8 + 2个字节,还需要1字节作为NULL标识列;因为该列是多个列共享的,如果该表只有一个字段,那么可以存储开销应该是11个字节,否则只能算作10.125字节(1/8等于0.125)

所以答案是10.125或11字节。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)

char和varchar有哪些区别?varchar最大长度是多少?相关推荐

  1. mysql in varchar_MySQL中char和varchar有啥区别?优缺点是啥?

    在mysql教程中char与varchar的区别呢,都是用来存储字符串的,只是他们的保存方式不一样罢了,char有固定的长度,而varchar属于可变长的字符类型. char与varchar的区别 c ...

  2. char、varchar、varchar2区别

    区别: 1.CHAR的长度是固定的,而VARCHAR2的长度是可以变化的, 比如,存储字符串"abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符), ...

  3. MySQL中的char和varcharmysql中varchar能存多少汉字、数字,以及varchar(100)和varchar(10)的区别

    1.varchar能存多少汉字.数字? 具体还是要看版本的,一个字符占用3个字节 ,一个汉字(包括数字)占用3个字节=一个字符 4.0版本以下,varchar(100),指的是100字节,如果存放UT ...

  4. oracle中char,varchar,varchar2的区别

    1.char长度固定,varchar2长度可变.例如,存储字符串"123",char(10)表示存储的内容占10个字节:123      (包含7个空格),varchar2(20) ...

  5. char varchar nchar nvarchar区别

    char varchar nchar nvarchar区别 char与varchar的区别 (1) char的长度是不可变的,而varchar的长度是可变的.(也就是说,定义一个char[4]和var ...

  6. MySQL中的char、varchar(10)、varchar(1000)的区别

    这里写自定义目录标题 tips MySQL中的varchar与char的区别 varchar(100) 与varchar(1000)的区别 参考 tips 若无特殊指明,文中提到的存储空间指的都是占用 ...

  7. TEXT、TINYTEXT、MEDIUMTEXT、LONGTEXT选择 和 char varchar varchar2 的区别

    TEXT.TINYTEXT.MEDIUMTEXT.LONGTEXT的区别: 储存不区分大小写的字符数据 TINYTEXT 最大长度是 255 (2^8 - 1) 个字符. TEXT 最大长度是 655 ...

  8. char(nchar)与varchar(nvarchar)的区别以及选择

    char(或是nchar)与varchar(或是nvarchar)是数据库中最常见的用于存储字符数据的数据类型,由于它们大多数特性相同,因此在选择使用哪种类型字符数据时,可能会有一些疑惑.不过,通过一 ...

  9. varchar和Nvarchar区别 ----转载

    varchar和Nvarchar区别 Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示 如果还为了这个纠结,就直接看看后面的解 ...

  10. varchar和Nvarchar区别

    Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示 如果还为了这个纠结,就直接看看后面的解说,做决定吧. 一般如果用到中文或者其 ...

最新文章

  1. 大三后端暑期实习面经总结——SSM微服务框架篇
  2. mysql 查询用户最后登陆时间_弄懂mysql:mysql的通信协议
  3. BIT的浅谈,简单理解
  4. Sql:成功解决将sql输出的datetime时间格式转为常规格式
  5. .Net面试题(3)
  6. Android Studio3.0中dependencies依赖由compile变为implementation的区别
  7. 转自JIM Wang:把 isv.config.xml 按钮事件移动到 entity.onload()
  8. 「C语言」指针数组 数组指针 指针函数 函数指针
  9. 亚马逊面试有几轮_经过几个月的Google面试准备,我被亚马逊录用
  10. 范灵俊(1983-),男,博士,中国科学院计算技术研究所工程师,信息技术战略研究中心战略研究主管,美国韦恩州立大学访问学者。...
  11. MYSQL(3)---MySQL的基本概念介绍
  12. 大智慧705服务器文件夹,大智慧2文件目录结构.doc
  13. windows 下 c++ 二维码生成库
  14. VBA-自动筛选符合条件的数据
  15. 【独行秀才】macOS Big Sur 11.6.5正式版(20G517)原版镜像
  16. 微信公众平台菜单编辑php,如何使用Vue.js实现微信公众号菜单编辑器(思路详解)...
  17. c语言md5函数 linux,【转】MD5校验C语言实现源代码
  18. 游戏公司盯上了区块链:是机会,还只是一场游戏?
  19. iOS之深入解析App的架构设计
  20. PDF to Word Document Converter for Mac(PDF文档转换器)

热门文章

  1. 红外遥控编解码注意事项
  2. 为什么学卫生管理要学c语言,卫生信息管理专业是学什么的
  3. 设置-关于手机里面硬件版本为空,如何完美甩锅
  4. (9)调整图像的亮度和对比度
  5. 如何在termius安装linux窗口,termius怎么使用?termius for mac使用ssh命令登陆服务器的方法教程...
  6. 感应电动机的启动压降计算
  7. Stable Diffusion搭建全过程记录,生成自己的专属艺术照
  8. 女生应该找一个会打DOTA有会玩ACM的男生当老公
  9. 测试策略模板——Test Strategy(中英文)
  10. JAVA菜鸟成长记——JNDI