热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【256期】MySQL中varchar最大长度?char和varchar有什么区别?

点击上方“Java精选”,选择“设为星标”别问别人为什么,多问自己凭什么!下方有惊喜,留言必回,有问必答&#x

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

别问别人为什么,多问自己凭什么!

下方有惊喜,留言必回,有问必答!

每一天进步一点点,是成功的开始...

以我多年经验来看,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

VARCHAR的定义

VARCHAR是变长字符串。

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

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

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

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

  • 另外,公众号Java精选,回复java面试,获取最新mysql面试题资料,支持在线随时随地刷题。

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

推荐下自己做的 Spring boot 的实战项目:
https://gitee.com/yoodb/jing-xuan‍

CHAR和VARCHAR具体对比:

9b27d68c14a1e372fbdd61bee077da10.png

如果开启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

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的长度标识位

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

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

推荐下几个月熬夜整理的近 10000+ 面试资料大全:https://gitee.com/yoodb/eboo‍ks

存储开销是小于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)

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

c538b084845e440373e228f1abde3ada.png

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

  • 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字节。

版权声明:本文为CSDN博主「王大雄_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

https://learn.blog.csdn.net/article/details/103341778

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

------ THE END ------

e3e2857cd757931915fae8a4a4cbc5bb.png精品资料,超赞福利!329faa46afd1b5e8a6adb8e88fbb1636.png

>Java精选面试题<
3000&#43; 道面试题在线刷&#xff0c;最新、最全 Java 面试题&#xff01;

52b04c187d02b80d88df611747d1b8ab.png

bb1d95afc2349dd954b6bcf1c4b808f9.png

期往精选  点击标题可跳转

【248期】如何在 Spring Boot 中使用异步方法优化 Service 逻辑提高接口响应速度?

【249期】Mybatis 源码解读 — 9 种设计模式&#xff0c;真是太有用了&#xff01;

【250期】面试官问&#xff1a;谈一谈 MQ 消息幂等&#xff08;去重&#xff09;通用解决方案&#xff1f;

【251期】分享一款基于 SpringBoot 和 ElementUi 的 HC 小区物联网平台&#xff0c;附源码&#xff01;

【252期】爆赞&#xff0c;对 volatile 关键字讲解最好的一篇文章&#xff01;

【253期】京东二面&#xff1a;商品库存的扣除过程中&#xff0c;如何防止超卖问题&#xff1f;

【254期】这些 SQL 语句真是让我干瞪眼&#xff01;

【255期】面试官问&#xff1a;MyBatis 二级缓存&#xff0c;如何实现关联刷新功能&#xff1f;

ceb6796a745ca571cb507af64bf3b2a0.png 技术交流群&#xff01;9b768183bef05d895d61db7480cbc266.png

最近有很多人问&#xff0c;有没有读者交流群&#xff01;想知道如何加入&#xff1f;方式很简单&#xff0c;兴趣相投的朋友&#xff0c;只需要点击下方卡片&#xff0c;回复“加群”&#xff0c;即可无套路入交流群&#xff01;

文章有帮助的话&#xff0c;在看&#xff0c;转发吧&#xff01;


推荐阅读
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 导航:网站首页谁有大一C语言考试题?快考试了,跪求题库有谁会做C语言的题目谁有大一C语言考试题?快考试了,跪 ... [详细]
  • 2017-08-2621:44:45writer:pprpRMQ问题就是区间最大最小值查询问题;这个SparseTable算法构造一个表, ... [详细]
  • 本文详细解析了JavaScript中相称性推断的知识点,包括严厉相称和宽松相称的区别,以及范例转换的规则。针对不同类型的范例值,如差别范例值、统一类的原始范例值和统一类的复合范例值,都给出了具体的比较方法。对于宽松相称的情况,也解释了原始范例值和对象之间的比较规则。通过本文的学习,读者可以更好地理解JavaScript中相称性推断的概念和应用。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • JavaScript设计模式之策略模式(Strategy Pattern)的优势及应用
    本文介绍了JavaScript设计模式之策略模式(Strategy Pattern)的定义和优势,策略模式可以避免代码中的多重判断条件,体现了开放-封闭原则。同时,策略模式的应用可以使系统的算法重复利用,避免复制粘贴。然而,策略模式也会增加策略类的数量,违反最少知识原则,需要了解各种策略类才能更好地应用于业务中。本文还以员工年终奖的计算为例,说明了策略模式的应用场景和实现方式。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 2018深入java目标计划及学习内容
    本文介绍了作者在2018年的深入java目标计划,包括学习计划和工作中要用到的内容。作者计划学习的内容包括kafka、zookeeper、hbase、hdoop、spark、elasticsearch、solr、spring cloud、mysql、mybatis等。其中,作者对jvm的学习有一定了解,并计划通读《jvm》一书。此外,作者还提到了《HotSpot实战》和《高性能MySQL》等书籍。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 设计完成后,将所完成的作品交由老师检查。管理进程接收申请进入的信号,在消息队列中取下申请进入队列的用户进程的信息,针对当前临界区状态,写一个回馈信息 ... [详细]
  • 如何在安卓应用中集成表情符号?原文:https://www. ... [详细]
author-avatar
阳光无限好1981
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有