当前位置: 首页 > 后端技术 > Java

面试官:MySQL中的varchar最多可以存多少个字符?大多数人都会弄错,..

时间:2023-04-01 13:29:25 Java

1。InnoDB是做什么的?InnoDB是一种存储引擎,将数据存储在磁盘上的表中。2、InnoDB是如何读写数据的?InnoDB处理数据的过程发生在内存中,需要将磁盘中的数据加载到内存中。如果是在处理写入或者修改请求,需要将内存中的内容刷新到磁盘中。读写磁盘的速度很慢,比读写内存差几个数量级,所以当我们想从表中获取一些记录时,InnoDB存储引擎将数据分成若干页,并使用“页作为磁盘和内存之间的链接”。交互的基本单位”,InnoDB中默认的pagesize是16KB。也就是说,正常情况下,一次至少有16KB的内容从磁盘读到内存,或者至少有16KB的内容在内存是一次性刷入磁盘的,所以用postman测试逐页查询接口时,发现第一次打印需要300~400ms,连续查找下一页需要30~40ms.有时候需要读磁盘,从磁盘加载16KB的数据到内存,然后会从内存中获取下一页的数据,不会再读磁盘,除非找不到在内存中的这16KB数据中,会再次读取磁盘将接下来的16KB数据取到内存中。(mysql8.0丢弃的querycache特性我就不讨论了,我测试过querycache是??关闭的mysql5.7,第一次还是慢,后面的查询很快,查询时间相差10倍左右。)?提醒:分页查询和数据库一个16KB页中的“页”是两个概念。?注意:innodb_page_size变量在服务器运行过程中不能改变,只能在第一次初始化MySQL数据目录时指定。所以页面的大小不能在运行时改变。3、关于varchar的问题千千万万——InnoDBrowformat?看到这里,你一定有和我一样的疑问,比如varchar(255)后的最大长度如何选择?为什么varchar(65535)不能,最大长度只能是varchar(16383)呢?我来给你展示!?我们通常以“记录”为单位向表中插入数据。这些“磁盘上的记录存储方式”也称为行格式或记录格式。有4种行格式,即Dynamic、Compact、Redundant和Compressed。MySQL5+默认的行格式是Dynamic,在MySQL5和MySQL8中已经验证。只关注动态的默认行格式”,让大家在日常开发中对varchar有更深入的了解。请记住这个表结构,后面我们会讲CREATETABLEtest(c1VARCHAR(10),c2VARCHAR(10)NOTNULL,c3CHAR(10),c4VARCHAR(10))CHARSET=utf8mb4;现在业务数据库的字符集都是utf8mb4,所以我就用这个来降低理解难度。插入测试(c1,c2,c3,c4)VALUES('aaaa','Hello','cc','d'),('eeee','fff',NULL,NULL);现在,表Therecordsin3.1dynamic——Innodb的默认行格式记录的附加信息是服务器为了描述这条记录而必须添加的一些附加信息。这些附加信息分为3类,即“可变长度字段长度列表”、“空值列表”和“记录头信息”。这里我只说“可变字段长度列表”和“NULL值列表”。因为记录头信息很绕,与本文关系不大。3.2innodb如何知道varchar到底有多长?——变长字段长度列表有些变长数据类型,如VARCHAR(M),各种TEXT类型,各种BLOB类型,变长数据类型字段存储多少字节的数据是不固定的。对于真正的数据,需要保存“这些数据占用的字节数”。就像设计String类型一样,它既是一个存储真实数据的char数组,也是一个记录字符串长度的length变量。另一个例子是输入框最大限制为500个字符,但你必须有一个变量来统计输入框中实际有多少个字符。同样,varchar也有一个记录实际数据长度的变量(“假设为L,后面为了方便描述会用到”)。L表示varchar实际占用的“字节数”,innodb最多分配2个字节来表示这个L。和unsignedshort类型一样,2个字节,寄存器最多有16位让你存放这个长度,所以L条记录的范围是2^16-1=65535。?这些变长字段(“比如varchar”)占用的存储空间分为两部分:真实数据内容部分,实际字节数被相应列占用,以及变长字段列表部分?下面以测试表中的第一条记录为例。因为测试表的c1、c2、c4列都是VARCHAR(10)类型,表示最多10个字符,所以记录中需要保存这三列的值的长度开头,》因为测试表中每一列使用的是utf8mb4字符集,每个字符最多需要4个字节来编码(不是用utf8而是utf8mb4因为可能存emoji表情,如果只是文本,utf8就够了)",我们看一下第一条记录中每个变长字段的内容长度:列名存储内容长度(十进制表示)内容长度(十六进制表示)c1'aaaa'4字节0x04c2'你好啊'9bytes0x09c4'd'1bytes0x01如何判断这些字段有多少字节?比如这里c2中的"Hello",使用下面的sql来确定SELECTLENGTH(c2)fromtestwherec1='aaaa';每个变长字段数据占用的“字节数”是按照列的顺序“倒序存储”的!!由于第一行记录中c1、c2、c4列的字符串比较短,也就是说varchar实际占用的字节数比较少,L可以用1个字节(8位)来表示,但是如果varchar实际占用的字节数比较大,L可能需要用2个字节(16位)来表示。varchar可以存储多少字节?继续阅读。3.3varchar(M)可以存储多少个字符,为什么提示最大为16383?首先我们要明白varchar(M)的M指的是字符数,不是字节数。为什么不能用varchar(20000)之类的,放不下20000个字符呢?为什么提示最多只能16383个字符?这个数字是如何计算的?我得和你谈谈这个!varchar是可变长度的,“varchar(64)”可以存储0~64个字符,不一定最多64个字符,谁知道这个类型可以存储多少个字符?innodb在设计的时候就已经考虑过了,只不过是以bytes为单位。后面我们可以根据对应的字符集转换成字符来理解。Innodb必须记录变长字段varchar实际占用的字节数L。如前所述,innodb最多分配2个字节(16位)的空间来记录这个L。?InnoDB有自己的一套规则。我们引入符号W、M和L:假设某个字符集需要“至多”W个字节来表示一个字符。utf8mb4字符集中的W是4utf8字符集。集合中的W在2ascii字符集中为1。对于变长类型VARCHAR(M),这种类型意味着它最多可以存储M个字符(注意字符不是字节),所以这种类型最多可以表示的字节数是M×W。假设数它实际存储的字符串占用的字节数是L。?看极端边界情况,innodb为了记录varchar实际存储了多少个“字节”,分配了最多2个字节的空间来记录。2字节16位,全部为1,最大可以记录的个数是2^16-1是65535,innodb最多可以记录varchar占用的字节数是65535,utf8mb4字符集的一个字符是最多4个字节,65535/4=16383.75,只要varchar字符数不超过16383个,innodb可以记录实际占用的长度L,不能再记录了!所以我可以解释一下刚才的图,varchar(20000)不行,最大是16383个字符“But!这里有butts要强调一下!”“一行的最大长度为65535字节”,一行中有很多东西,包括长字段的变量List,NULL值的列表,记录头信息。你要考虑如果字段允许为NULL,NULL值列表会占用一个字节(只要不超过8个字段),每个列字段的变长字段的实际长度会花费1~2个字节。如果这个字段的数据太大,就会变成溢出列,这个字段的数据会被分成很多行存储(后面会讲到,大家可以看完再回来看这个例子NULL值列表和溢出列)。所以即使提示16383个字符,你存到16383也是绝对不可能的。我一直在这个字段中添加字符以保存测试,最后发现这些字符的总长度达到了限制,即48545字节。如果超过48545字节,这里会报错,多一个字符会报错,远远小于65535字节,少了1W多字节。主要原因是由于溢出列,数据分散在不同的行中。所以对于很长的数据,建议考虑文本类型。从这个现象可以看出varchar(M)的M很大,实际上是达不到M的边界值的。下面来解释一下规则(解释中的字符集使用utf8mb4,W=4)规则1:如果最大允许存储的字节数M×W<=255,》varchar实际占用的字节数L只分配1个字节给?有人说最大允许存储的字节数M×W<=255,即当允许存储的最大字符数<=?255/4?="63"时,varchar实际占用的字节数L只分配1个字节即可表示。这个结论对吗?显然是错误的,因为这里255/“4”,你怎么知道每个存储的字符是4个字节?emoji表情是不是都保存了?没有字母也没有汉字?InnoDB读取变长字段的时候对于一条记录的长度列表,它首先检查表结构,如果一个变长字段允许存储的最大字节数不大于255,则只用1个字节来表示真正数据占用的字节数。?规则2:如果允许存储的最大字节数M×W>255,有两种情况:如果实际存储字节数L<=127,则varchar实际占用的字节数L只分配1个字节。(?…?表示向下取整)?有人说当实际存储字节L<=127时,即当实际存储字节<=?127/4?="31"时,varchar实际占用的空间字节数L可以通过仅分配一个字节来表示。这个结论正确吗?显然不对,因为这里127/“4”,你怎么知道实际存储的一个字符是4个字节呢?emoji表情都保存了吗?有没有字母或汉字??如果实际存储字节数L>127,则varchar实际占用的字节数L需要分配2个字节来表示。另外需要注意的是,变长字段列表只存储非NULL列的长度。表记录是这样的。对于第二条记录,c4列值为NULL,所以只能存储c1和c2列。第一条记录的变长字段长度列表部分占用3个字节的空间,因为有c1、c2、c4列,而且内容很小,每列实际占用的字节数可以用1字节表示,和为3个字节,第二条记录的变长字段长度列表部分占2个字节。当然,并不是所有的记录都有这个“变长字段长度列表”部分。比如表中“所有列都不是变长数据类型”或者“所有列值都是NULL”,这部分就不需要了。在实际业务开发中,几乎没有用到varchar,所以实际开发中的记录会有“可变字段长度列表”3.4节记录为NULL,innodb如何处理?——NULL值列表仔细看这里,一定是高手。如果你和我一样,开发规范中不推荐NULL,一般写NOTNULL。其实记录中没有NULL值的列表,这样也节省了空间。如果表中有些列可能存储NULL值,将这些NULL值存储在“记录的真实数据”中会占用大量空间,所以动态行格式对这些具有NULL值的列进行管理,并存储在NULL中在值列表中,其处理过程如下:统计表中允许哪些列存储NULL。主键列和被NOTNULL修饰的列不能存储NULL值,因此这些列在统计时不会被统计在内。比如表test的c1、c3、c4三列都允许存储NULL值,而c2列被NOTNULL修饰,不允许存储NULL值。“如果表中没有允许存储NULL的列,则NULL值列表不存在。”否则,每一个允许存储NULL的列对应一个二进制位,二进制位按照列的顺序“倒序排列”。当二进制位的值为1时,表示该列的值为NULL,为0时,表示该列的值不为NULL。因为表test的c1、c3、c4都是允许存储NULL值的NULL-allowed列,所以这三个列和二进制位的对应关系如下:NULL值的列表必须是用整数字节数表示,如果使用的二进制位数不是整数字节数,则字节的“高位”补0。也就是说,表test中只允许有3个字段为NULL,对应3位二进制数,如果小于1字节,则高位补0即可。以此类推,如果表中有9个字段允许NULL,则NULL值列表这条记录需要2个字节来表示,高字节补0。对于“第一条记录”,c1、c3、c4不为NULL,对应的基数为0,十六进制表示为0x00。对于“第二条记录”,c3和c4都为NULL,对应的二进制位为1,十六进制表示为0x06。填充NULL值列表后,这两条记录的示意图如下:3.5A列被数据占用字节数很大怎么办?——动态行格式中的溢出列如果某一列存储的数据占用的字节数非常多,则该列可以称为溢出列。对于一个占用大量存储空间的列,在记录真实数据时,“该列只会使用20字节的空间”,而这20字节的空间并不存储数据,因为数据是分散存储在其他几个行。这20字节的空间存放了散列的地址和占用的字节数。分散的行记录是由单个链表连接的结构。?后续:如果你对innodb存储结构的其他行格式,或者我没有提到的记录头信息感兴趣,可以看看这本书《MySQL是怎样运行的》。我和书上的区别在于书上的压缩格式是ascii,我选择平时开发使用的默认动态格式。字符集是utf8mb4。字符集更改后,我重新计算了文本和图中的所有数据。可能你不注意行格式,所以按照动态格式理解即可,更贴近实际开发。来源:https://blog.csdn.net/qq_3411...近期热点文章推荐:1.1,000+Java面试题及答案(2022最新版)2.精彩!Java协程来了...3.SpringBoot2.x教程,太全面了!4.不要写爆满屏的类,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!