3.3 varchar(M) 能存多少个字符,为什么提示最大16383?
首先要理解varchar(M)的M是说字符个数,而不是字节。
为什么不能varchar(20000)之类的,是20000个字符放不下吗?
为什么提示只能最大16383个字符呢?这个数字是怎么算出来的?
这个我就得和你好好唠嗑了!
varchar是变长的,「varchar(64)」 能存放0~64个字符不等,并不一定是存了最大64个字符,谁知道这个类型到底存了几个字符呢?innodb设计的时候,就已经考虑到了,不过是用字节作为单位,后续我们可以根据对应字符集转变为字符来理解,innodb必须记录变长字段varchar真实占用的字节数L。前面说过了,innodb最多分配2个字节(16个bit位)的空间去记录这个L。
❝
InnoDB有它的一套规则,我们引入W、M和L这几个符号:
- 假设某个字符集中「最多」需要
W字节来表示一个字符
utf8mb4字符集中的W就是4utf8字符集中W就是3gbk字符集中的W就是2ascii字符集中的W就是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个字符
「但是!这里强调是有但是的!」
「行最大长度是65535字节」,行里面有很多东西,包括变长字段列表、NULL值列表、记录头信息。你得考虑该字段如果允许为NULL,NULL值列表会占用一个字节(只要没超过8个字段),每一列字段的变长字段实际长度会花费1~2个字节,如果该字段的数据太大,会变成溢出列,该字段的数据会分成很多行存储(后面会讲,你可以看完NULL值列表和溢出列后再回来看这个例子)。所以即便提示16383个字符,你也绝对不可能存到16383。
我做了个测试
create table t2 ( name varchar(16383))charset=utf8mb4;
不断往这个字段添加字符保存测试,最后发现,这些字符总长度到极限也就是48545字节。
这里48545个字节,再多一个字符就会报错,远不到65535字节,差了1W多字节。主要是因为溢出列的原因,数据分散在不同的行中,所以,很长的数据,建议往text类型考虑。这个现象可以看出,varchar(M)的M很大,实际是达不到M这个边界值的。
下面说明一下规则(讲解中字符集用utf8mb4,W=4)
规则一:如果允许存储的最大字节数M × W <= 255,「varchar占用的真实字节数L只分配1个字节来表示。」
❝
有人说,允许存储的最大字节数M × W <= 255,即允许存储的最大字符数 <= ⌊255 / 4⌋ = 「63」个时,varchar占用的真实字节数L仅分配1个字节就能表示。这个结论正确吗? 显然错误,因为这里255 / 「4」,你怎么知道每个存储的一个字符是4个字节呢?难道全部存的emoji表情?不存字母汉字啥的?InnoDB在读记录的变长字段长度列表时先查看表结构,如果某个变长字段允许存储的最大字节数不大于255时,只用1个字节来表示真实数据占用的字节。
❞
规则二:如果允许存储的最大字节数M × W > 255,则分为两种情况:
如果实际存储字节L <= 127,varchar占用的真实字节数L仅分配1个字节就能表示。(⌊ … ⌋表示向下取整)
❝
有人说,实际存储字节L <= 127,即「实际存储字符」 <= ⌊127 / 4⌋ = 「31」个时,varchar占用的真实字节数L仅分配1个字节就能表示。这个结论正确吗? 显然错误,因为这里127 / 「4」,你怎么知道实际存储的一个字符是4个字节呢?难道全部存的emoji表情?不存字母汉字啥的?
❞
如果实际存储字节L > 127,varchar占用的真实字节数L需要分配2个字节才能表示。
另外需要注意的是,变长字段列表只存储非NULL的列的长度。
表记录是这样的
对于第二条记录,c4列值为NULL,所以只存储c1和c2列即可。
第一条记录的变长字段长度列表部分占用3字节空间,因为有c1、c2、c4列,且内容都很少,每列真实占用字节数用1个字节可以表示,加起来就是3个字节,第二条记录变长字段长度列表部分占用2字节。
当然,并不是所有记录都有这个「变长字段长度列表」部分,比方说表中「所有的列都不是变长的数据类型」或者 「所有列的值都是NULL」 的话,这一部分就不需要有。实际业务开发中,几乎没有不使用varchar的,所以实际开发中的记录都会有「变长字段长度列表」部分




