大家好,我是小林。上周,我发了一篇文章Byteside:HowisHowistheNULLvalueofMySQLstored?,文章提到了这个问题:“varchar(n)中n的最大值是多少?”当时这部分内容不够严谨,所以重写了这部分。那么,这次就来说说这个问题吧。前置知识要回答这个问题,首先要知道MySQL中一条记录的格式是什么样的。以紧凑行格式为例,它是这样的:可以看到,一条完整的记录分为两部分:“记录的额外信息”和“记录的真实数据”。这里重点关注记录的附加信息,它由3部分组成:变长字段长度列表、NULL值列表、记录头信息。变长字段长度列表用于存储一行记录中每个变长字段的长度。“可变字段长度列表”占用的字节数=所有“可变字段长度”占用的字节数之和。例如,假设数据库表中有两个varchar(10)类型的字段,分别是a和b,数据库表的字符集是ascii字符集(1个字符占1个字节)。那么a和b字段的数据值的长度只需要用1个字节来表示,因为1个字节最多可以表示的字节数是255,而最多可以存储的字节数在一个varchar(10)类型的字段中是10个字节,所以只需要用1个字节来表示变长字段的长度即可。那么本例中“可变字段长度列表”占用的字节数=1字节+1字节=2字节。“可变字段长度列表”不是必需的。如果数据库表没有变长字段,比如字段类型都是int,那么行格式就不需要“变长列表”。NULL值列表用于标记一行记录中字段值为NULL的字段。当二进制位的值为1时,表示该字段的值为NULL;当该位的值为0时,表示该字段的值不为NULL。此外,NULL值列表必须由整数字节数表示(1字节为8位)。如果使用的二进制位数小于整数个字节,则在字节的高位添加0。如果表中允许NULL值的记录数小于等于8,那么NULL值的列表将用1个字节来表示。如果表中允许NULL值的记录数大于8且小于等于16,那么NULL值的列表将用2个字节表示,以此类推。因此,如果表中有允许NULL的字段,那么“NULL值列表”至少要占用1个字节的空间。“NULL值列表”不是必需的。如果数据库表中的字段都定义为NOTNULL,则行格式中不需要“NULL值列表”。记录头信息记录头信息包含了很多内容,比如记录的删除标志位,指向下一条记录的指针等,这些都不是本文的重点,不再赘述。varchar(n)中n的最大值是多少?我们需要明确一点,MySQL规定除了TEXT、BLOB等大对象类型外,其他所有列(不包括隐藏列和记录头信息)占用的字节总长度不能超过65535字节。也就是说,除了TEXT和BLOBs类型的列,一行记录的最大限制是65535字节。请注意,它是行的总长度,而不是列的总长度。知道了这个前提,我们再来看一下这个问题:“varchar(n)中n的最大值是多少?”varchar(n)字段类型的n表示最大存储的字符数,而不是字节大小。.要计算varchar(n)最多能存多少字节,还要看数据库表的字符集,因为字符集代表一个字符占多少字节。比如ascii字符集中,1个字符占1个字节,那么varchar(100)表示最多可以存储100个字节的数据。在单个字段的情况下,我们知道一行记录最多只能存储65535字节的数据。假设数据库表只有一列,类型为varchar(n),字符集为ascii。这样的话,varchar(n)中n的最大值是不是65535呢?先别急着下结论,我们先做个实验来验证一下。我们定义了一个varchar(65535)类型的字段和一个字符集为ascii的数据库表。CREATETABLEtest(`name`VARCHAR(65535)NULL)ENGINE=InnoDBDEFAULTCHARACTERSET=asciiROW_FORMAT=COMPACT;查看是否可以成功创建表:结果显示创建失败。从报错信息中我们可以知道,一行数据的最大字节数是65535(不包括TEXT、BLOB等大对象类型),其中包含了存储开销。问题是,这个存储开销是多少?其实就是“可变字段长度列表”和“NULL值列表”。也就是说,一行数据的最大字节数是65535,实际上包括了“可变长度字段长度列表”和“NULL值列表”占用的字节数。因此,我们在计算varchar(n)中n的最大值时,需要减去存储开销占用的字节数。这是因为我们在存储字段类型为varchar(n)的数据时,实际上是分成三部分进行存储的:真实数据占用的字节数和NULL标志。如果不允许NULL,这部分不需要这种情况,NULL值列表占用的字节数是多少?我之前建表的时候允许字段为NULL,所以会用1个字节来表示“NULL值列表”。在这种情况下,“可变字段长度列表”占用的字节数是多少?“可变字段长度列表”占用的字节数=所有“可变字段长度”占用的字节数之和。因此,我们首先要知道需要多少字节来表示每个变长字段的“变长字段长度”?具体情况分为:条件一:如果变长字段允许存放的最大字节数小于或等于255字节,则用1字节表示“变长字段长度”;条件二:如果变长字段允许存储的最大字节数大于255字节,则用2个字节表示“变长字段长度”;这里的字段类型是varchar(65535),字符集是ascii,所以表示变长字段允许存放的最大字节数是65535,满足第二个条件,所以2个字节会用于表示“可变长度字段长度”。因为我们的案例只有1个变长字段,所以“变长字段长度列表”=1个“变长字段长度”占用的字节数,即2个字节。因为我们在varchar(n)中计算n的最大值时,需要减去“可变字段长度列表”和“NULL值列表”所占用的字节数。因此,在数据库表只有一个varchar(n)字段,字符集为ascii的情况下,varchar(n)中n的最大值=65535-2-1=65532。我们先测试一下,看varchar是否(65533)可行吗?可以看出,还是不行,我们看看varchar(65532)可行不可行?可以看到,创建成功。说明我们的推导是正确的。varchar(n)中计算n的最大值时,需要减去“可变字段长度列表”和“NULL值列表”占用的字节数。当然,我上面的例子是针对字符集是ascii的情况。如果使用UTF-8,varchar(n)可以存储的最大数据的计算方法不同:在UTF-8字符集中,一个字符串最多可以要求三个字节,n的最大值在varchar(n)就是65532/3=21844,上面说的只是针对某个字段的计算方式。多字段情况下,如果有多个字段,保证所有字段的长度+变长字段字节列表占用的字节数+NULL值列表占用的字节数<=65535。这里是多字段情况的一个例子。实验结果:总结一下varchar(n)中n的最大值是多少?一行记录最多可以存储65535字节的数据,但这包括“变长字段字节列表占用的字节数”和“NULL值列表占用的字节数”。因此,我们在计算varchar(n)中n的最大值时,需要减去这两个链表占用的字节数。如果一张表只有一个varchar(n)字段,并且允许NULL,则字符集为ascii。varchar(n)中n的最大值为65532。计算公式:65535-变长字段字节列表占用的字节数-NULL值列表占用的字节数=65535-2-1=65532.如果有多个字段,保证所有字段的长度+变长字段字节列表占用的字节数+NULL值列表占用的字节数<=65535。
