0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

mysql的varchar字段最大长度为什么不是65535而是65533呢?

dyquk4xk2p3d 来源:小白debug 2023-04-19 11:30 次阅读

在mysql建表sql里,我们经常会有定义字符串类型的需求。

CREATETABLE`user`(
`name`varchar(100)NOTNULLDEFAULT''COMMENT'名字'
)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

比方说user表里的名字,就是个字符串。mysql里有两个类型比较适合这个场景。

char和varchar。

声明它们都需要在字段边上加个数组,比如char(100)varchar(100),这个100是指当前字段能放的最大字符数

char和varchar的区别在于,varchar虽然声明了最大能放100个字符,但一开始不需要分配100个字符的空间,可以根据需要慢慢增加空间。而char一开始声明是多少,就固定预留多少空间。

所以,varchar比起char更省空间,一般没啥大事,大家都爱用varchar

那问题来了,声明varchar字段时,它的最大长度是多少呢?

相信大家应该听说过varchar字段的最大长度是65535吧。

没听过也没关系,你现在听到了。

但实际上是这样吗?

我们来做个实验。

varchar最大值是多少

我们直接拿65535来试一下。

e4041d80-de60-11ed-bfe3-dac502259ad0.png长度为65535的varchar报错

很明显报错了。

报错内容也说了,由于列长度过大导致报错,最长是16383

把上面的65535改成 16383,确实是成功了。

哦?所以说varchar最大值是16383?

当然不是。

这其实还有好几个因素影响这这个最大值。

不同字符集的影响

varchar里放的是字符串,而字符串看起来可以是英文字母,也可以是数字或中文。但不管怎么样,都可以把这样的中英文数字转成二进制的01串。

按照一定规则把符号和二进制码对应起来,这就是编码。而把n多这种已经编码的字符聚在一起,就是我们常说的字符集

建表语句里有个CHARSET,这里填的是字符集。

不同的字符集要求使用的字节个数也不同,我们可以通过show charset;看到mysql支持哪些字符集,以及这些字符集里存储一个字符所需的最大字节数(Maxlen)。

e4162098-de60-11ed-bfe3-dac502259ad0.png查看mysql支持哪些charset

我们尝试下把建表sql语句里的CHARSET改一改,比如改成utf8mb3

我们再执行下,会发现,最大值又不一样了。

e428a72c-de60-11ed-bfe3-dac502259ad0.pngutf8mb3下的报错

并且,上面虽然提示max=21845,但要是真执行起来会发现还是报错。在改为21844之后才成功。

不讲武德。

再把字符集改为 latin1。会发现,最大值会是 65533

e43aa242-de60-11ed-bfe3-dac502259ad0.pngvarchar为65533时创建成功

这里渐渐可以发现规律。

utf8mb4的maxlen=4,对应varchar最大长度=16383。4*16383 = 65532。

utf8mb3的maxlen=3,对应varchar最大长度=21844。3*21844 = 65532。

latin1的maxlen=1,对应varchar最大长度=65533。 1 * 65533 = 65533。

也就是说varchar边上的长度代表的是这一列能放的最大字符数,而maxlen代表单个字符占用的最大字节数。相乘的结果很接近65535。说明65535是指的字节数,而不是字符数

也就是说varchar的最大长度,根据选择的字符集的不同,会有区别。

总的来说接近于 65535 除以 字符集的maxlen。

但其实这样还不够严谨。还有其他影响因素。

是否可以为NULL的影响

上面的建表语句里声明了test字段都是NOT NULL,也就是非空,如果我们将这个改成可以为NULL,再用CHARSET=latin1去试试。这时候就会发现,前面NOT NULL的时候最大能使用65533去建表,现在报错了。

改成65532,就能成功了,也就是最长长度少了1个字节

e450aec0-de60-11ed-bfe3-dac502259ad0.png是否为NULL的影响

这是因为一个字段是否为NULL这件事情,是需要一个字节去记录下来的。

而当字段为NOT NULL的时候,则可以省下这个字节。

列数的影响

上面提到的情况都是在表里只有一列时的结果,当我们表里有更多的列时,我们会发现varchar的最大值还会有变化。比如同样还是latin1字符集,我们再增加一列varchar类型,并且用的还是前面允许的最大值65533。

结果发现这次会失败。

e45f3b8e-de60-11ed-bfe3-dac502259ad0.png两个varchar列的情况

查了一下资料发现,原来65535是mysql单行的最大长度(不包含blob和text等类型的情况下)

mysql表里单行中的所有列加起来(不考虑其他隐藏列和记录头信息) ,占用的最大长度是65535个字节。

注意上面加粗的部分,加起来不超过65535。

比如如果还有int的列,那它占用4个字节,bigint占用8个字节,字段越多,留给单个varchar列的空间就越少。

因此,前面提到的 varchar 的最大长度,接近于 65535 除以 字符集的maxlen,但前提是只有一列not null 的varchar类型的字段。

为什么不是65535而是65533?

不过问题又来了,上面建表sql里,不管是那种字符集,最后得到的字符数都约等于65533。

但数据库单行最大值应该是65535。65535 - 65533 = 2 。这里面还差了个2,为什么呢?

这就要聊一下mysql单行里数据到底是怎么存储的。

数据表行存储的格式

我们可以通过show table status命令,查看到当前表格使用的行格式。

e47537cc-de60-11ed-bfe3-dac502259ad0.png查看到当前表格使用的行格式

通过上面的Row_format字段可以看到这个表用的是Dynamic行格式。

事实上,现在的mysql数据表一般都是采用Dynamic行记录格式。

我们来看下Dynamic行格式长什么样子。

e487c68a-de60-11ed-bfe3-dac502259ad0.pngDynamic行记录格式

Dynamic格式将行记录分为两部分,分为是行记录的额外信息行记录的真实数据

行记录的额外信息:

变长字段长度列表:指的是varchar,text,blob这种类型,它们属于变长字段,这里表示的就是这些字段的长度。

NULL值列表:用来记录当前行里哪些列是为null的。如果全部列都是not null的话,那就不需要有这个字段。

记录头信息:这是固定5个字节,用来记录一些特殊的信息,比如这一行是否被删了,这一行在这个16k的数据页内是不是最小的,以及指向下一条记录的指针之类的一些信息,不需要太关注。

行记录的真实数据:

里面放的就是一行里,每一列的真正内容。除了我们建表时里涉及到的列以外,还有一些隐藏列。

比如Row_ID,这个是在建表是没有声明主键时,数据表自动会生成的隐藏主键。另外还有trx_id字段,用于记录当前这一行数据行是被哪个事务修改的,和一个roll_pointer字段,这个字段是用来指向当前这个数据行的上一个版本,通过这个字段,可以为这行数据形成一条版本链,从而实现多版本并发控制(MVCC)。有没有很眼熟,这个在之前写的文章里出现过。

e4a97cbc-de60-11ed-bfe3-dac502259ad0.png隐藏列有哪些

所以我们回过头来看我们建的表,当只有一列not null的 varchar字段时,行记录长下面这样。

e4c02f2a-de60-11ed-bfe3-dac502259ad0.png单条varchar数据的Dynamic行记录格式.drawio

前面提到,行最大值65535字节是不包含隐藏列和记录头信息的,所以其实是指上图中红色的部分。

而最左边的变长字段长度列表中,为了表示varchar列的长度,占用了两个字节,也就是16位,2的16次方,最大可以表示65535的长度,正好足够用来表示varchar列当前的长度是65533。

所以65535 - 65533 = 2 。这里面差的2,是用来存varchar字段长度去了。

一个页才16k,怎么保存65533(64k)数据?

之前的文章里其实多次提到了mysql底层是以页的形式去存储数据的,而一个页固定16k,而一个varchar字段最大能放65533字节数据,换算一下大概是64k,整整4个16k的页。

e4d64332-de60-11ed-bfe3-dac502259ad0.png页结构

这里面是怎么实现的?

对于这种情况,其实行数据里针对这个超大的varchar字段只保存个20字节的指针(实际上是个偏移量),这个指针会指向新的页(off page),这些页里保存的是实际的varchar字段里的65533字节数据。这种由于字段过长导致需要额外的页来保存数据的现象叫行溢出

e4e9cdf8-de60-11ed-bfe3-dac502259ad0.png行溢出

大于64k的字符串该怎么处理?

如果离谱点,数据量更大,比64k还大,这时候就不能继续用varchar了,需要改用text和blob类型字段。

而text和blob类型本身也是分TINY、MEDIUM,LONG三个档位的,对应着不同的数据长度,最大到4G左右。

像下面这样就可以将数据类型定义为LONGTEXT。

CREATETABLE`test_max_length`(
`test`LONGTEXTNOTNULLCOMMENT'测试长度字段'
)ENGINE=InnoDBDEFAULTCHARSET=latin1;

而他们的存储方式也跟varchar的情况类似,只保存20个字节的指针,实际数据保存在其他溢出页里。

以前我们查某一行数据,他们都在一个16k的数据页里,查询时只要一次磁盘IO就能将这个数据页读取出来。

当一个数据库里某行数据里有个特别大的字符串时,我们如果还想把整行数据给读出来,那我们还得把off page的数据给全部读出来,这意味着更多的磁盘IO,性能就更差了

为了规避这个问题,我们写select sql的时候,如果发现某列字段,是个特别长的字符串时,能不读它就尽量不加到select里,这也是为什么大家不建议使用select * from table的原因。

blob和text的区别

一般来说,blob和text都可以用来放超长字符串。但它们会有一点点区别。

我们知道字符集(charset)下还有个校对规则(collation)的概念,比如同样是a,大写A和小写a能不能算作是一个字符,这会影响比较和排序,collation就是定义这个规则用的。

blob没有字符集的概念,而text有。这意味如果用blob来存文本的话,就没法用字符集的校对规则来排序和做比较。

还有一个区别,blob还能保存二进制数据,比如压缩过的文本数据,图片或者视频,别笑,虽然不合适,但我确实见过有人拿它来保存视频。。。

总结

现在的mysql数据表一般采用Dynamic行记录格式。它由行记录的额外信息和行记录的真实数据组成。

mysql表里单行中的所有列加起来(不考虑其他隐藏列和记录头信息) ,占用的最大长度是65535个字节。

如果数据表里只有一列 not null的varchar字段,它的最大长度,接近于65535 除以 字符集的maxlen

如果要存放大于64k的字段数据,可以考虑使用longtext和longblob等类型。

mysql的数据页大小是16k,为了保存varchar或者text,blob这种长度可能大于16k的字段,在Dynamic行格式中,会只保留20个字节的指针,实际数据则放在其他溢出页中。为了将它们读取出来,会需要更多的磁盘IO。

blob和text很像,但blob没有字符集的概念,并且还能存放二进制的数据,比如图片或视频,但实际上图片和视频更推荐放在对象存储(ObjectStorageService,简称oss)中。






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编码器
    +关注

    关注

    42

    文章

    3461

    浏览量

    132255
  • MySQL
    +关注

    关注

    1

    文章

    777

    浏览量

    26119
  • MYSQL数据库
    +关注

    关注

    0

    文章

    95

    浏览量

    9297

原文标题:mysql的varchar字段最大长度真的是65535吗?

文章出处:【微信号:良许Linux,微信公众号:良许Linux】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    DMA搬运的数据长度超过65535怎么处理?

    我有个问题请问下。我现在使用定时器更新事件触发DMA,采集1次SPI数据,使用的是循环模式。但是我现在采集的数据长度需要超过了65535,现在应该怎么处理?DMA搬运的数据长度最多是65535
    发表于 03-29 08:57

    MySQL笔记和小练习

    一、MySQL数据库系统MySQL数据库系统就是用来对数据库、数据的一些管理二、数据库系统1.数据库就是用来存储各种数据的2.数据库管理系统就是用来管理各种数据库的数据的一个系统三、常见的一些
    发表于 06-27 08:45

    xy波形图x轴可以不是时间,而是一组其他数据吗,比如长度,。。。

    xy波形图x轴可以不是时间,而是一组其他数据吗,比如长度,。。。
    发表于 10-19 10:00

    MySQL如何实现添加字段

    MySQL添加字段应该如何实现?这是很多刚刚接触MySQL数据库的新人都提到过的问题,下面就为您介绍MySQL添加
    发表于 07-11 07:17

    Mysql字段类型

    Mysql字段类型
    发表于 07-18 12:04

    请问mysql数据库一个汉字到底占几个字节?

    GBK 和 UTF-8 字符集我都测试了,汉字在 MYSQL 里都占一个字节,例如一个字段 varchar (2),就能存储两个汉字或两个字母或两个数字。但我在网上搜了半天,都说 MYSQL
    发表于 06-02 14:48

    MYSQL数据库如何创建计算字段以及怎么样从应用程序中使用别名

    计算字段存储在数据库表中的数据一般不是应用程序所需要的格式,例如:显示两个信息,但不是在用一个表不同列中,但程序需要把他们作为一个格式的字段检索出来列数据是大小混合,但程序需要把所以数
    发表于 11-04 14:40

    华为手机销量最高的不是P9也不是荣耀8 而是这台!

    目前出货量最大的国产手机,不是高端手机,也不是不是中端手机,而是千元机。
    发表于 02-15 13:50 3112次阅读

    MySQL字段选择合适数据类型

    在使用MySQL创建数据表时,经常会遇到如何为字段选择合适的数据类型的问题,接下来我们一起分析字符串、数值、日期数据类型的选择。
    的头像 发表于 05-03 17:38 2657次阅读
    <b class='flag-5'>MySQL</b>为<b class='flag-5'>字段</b>选择合适数据类型

    一百道关于MySQL索引解答

    数据库 1. MySQL索引使用有哪些注意事项? 可以从三个维度回答这个问题:索引哪些情况会失效,索引不适合哪些场景,索引规则 索引哪些情况会失效 查询条件包含or,可能导致索引失效 如何字段类型
    的头像 发表于 06-13 15:51 1933次阅读

    MySQLvarchar(n) 中 n 最大取值为多少?

    那么a和b字段的数据值的长度分别只需要用1字节表示就行了,因为1字节能表示最大的字节数是 255,而 varchar(10) 类型的字段
    的头像 发表于 12-05 14:07 580次阅读

    mysql经典面试题及答案

    char、varchar的区别是什么? varchar是变长而char的长度是固定的。如果你的内容是固定大小的,你会得到更好的性能。
    的头像 发表于 10-20 09:47 803次阅读
    <b class='flag-5'>mysql</b>经典面试题及答案

    clob类型转varchar方法

    CLOB类型和VARCHAR类型是数据库中常用的数据类型,用于存储可变长度的字符数据。CLOB类型用于存储大文本数据,而VARCHAR类型适用于存储较短的字符串数据。在某些情况下,我们可能需要
    的头像 发表于 11-21 10:33 3939次阅读

    CLOB类型的数据转换为VARCHAR类型

    VARCHAR字段则适用于存储小于或等于某个长度的字符数据。当我们需要将CLOB类型的数据转换为VARCHAR类型时,可以使用以下方法: 使用数据库函数:不同的数据库系统提供了不同的函
    的头像 发表于 11-21 10:39 3175次阅读

    oracle修改表字段长度语句

    Oracle 修改表字段长度可以通过使用 ALTER TABLE 语句来实现。在对表进行修改之前,我们需要先了解一些基本的概念。 表字段长度是指在数据库表中用来存储数据的列的最大容量。当我们需要存储
    的头像 发表于 11-21 11:34 1192次阅读