在mysql建表sql里,我们经常会有定义字符串类型的需求。
CREATE TABLE `user` (
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;比如用户表中的name就是一个字符串,MySQL中有两种类型比较适合这种场景。
炭和。
要声明它们,需要在字段旁边添加一个数组,例如 char(100) 和 (100),其中 100 指的是当前字段可放置的最大字符数。
char和的区别在于,虽然声明了最大容量是100个字符,但是一开始并不需要分配100个字符的空间,可以根据需要慢慢增加空间.但是char预留的空间在一开始是固定的。
因此它比char节省空间,而且一般也不会有什么大问题,所以大家都喜欢用它。
那么问题是,声明一个字段时,它的最大长度是多少?
我相信你听说过,字段的最大长度是65535。
如果你没听过也没关系,现在你已经听过了。
但事实真是如此吗?
我们来做一个实验。
最大值是多少?
我们直接试一下65535吧。
长度错误 65535
显然报了错误。
错误信息还说列长度太大,最长是16383。
将上面的 65535 改为 16383 确实成功了。
哦?那么最大值是 16383?
当然不是。
实际上有几个因素会影响这个最大值。
不同字符集的影响
字符串是一串字母、数字或者汉字。但不管怎样,你都可以把这样的中文、英文和数字转换成01的二进制字符串。
把符号按照一定的规则与二进制代码进行匹配就叫编码,把许多这样编码的字符放在一起就是我们常说的字符集。
建表语句里有字符集。
不同的字符集需要的字节数不同,我们可以通过show;来查看MySQL支持哪些字符集,以及在这些字符集中存储一个字符所需的最大字节数()。
查看 MySQL 支持哪些
我们尝试将创建表的SQL语句改为,例如。
我们再执行一次就会发现最大值又不一样了。
以下错误
另外,虽然上面显示的是max=21845,但实际尝试时仍然会出错。必须将其更改为21844后才能成功。
无武德。
然后把字符集改为 。你会发现最大值会是 65533。
当值为65533时表示创建成功
渐渐地,我们就能发现这里面的规律。
也就是说边的长度代表了这一列最多可以放多少个字符,代表了单个字符最多占用多少个字节,相乘的结果和65535非常接近,由此可见65535指的是字节数,而不是字符数。
也就是说,最大长度会根据所选的字符集而不同。
总体而言,它接近 65535 除以字符集。
但这还不够严谨,还有其他影响因素。
是否可以为NULL的影响
在上面的建表语句中,测试字段声明为NOT NULL,也就是非空,如果我们把这个改为NULL,再用=试试,会发现NOT NULL时能使用的最大值是65533,然后去建表,现在就报错。
如果改成65532的话就会成功,也就是说最大长度减少了1个字节。
是否为NULL的影响
这是因为一个字段是否为NULL需要一个字节来记录。
当该字段不为NULL时,可以保存该字节。
列数的影响
上面提到的情况都是当表中只有一列时的结果。当表中的列更多时,我们会发现最大值会发生变化。例如,如果我们在字符集中添加了一个列类型,并且允许的最大值是 65533。
事实证明,这一次是要失败了。
两列案例
查了资料后发现65535是MySQL中单行的最大长度(不包括blob、text等类型)
MySQL表的单行所有列的最大长度(不包括其他隐藏列和记录头信息)为65535字节。
注意上面加粗的部分,总数不超过65535。
比如同样有一个int型的列的话,就占用4个字节,如果有8个字节的话,那么字段越多,单个列所剩的空间就越少。
因此上面说的最大长度就接近65535除以字符集了,但是前提是not null类型字段的列只能有一列。
为什么是65533而不是65535?
但是问题又出现了,上面建表的SQL语句,无论使用哪种字符集,最后得到的字符数都约等于65533。
但数据库中单行的最大值应该是65535,65535-65533=2,这里少了一个2,这是为什么呢?
这就需要讨论一下MySQL中数据是如何存储在单行中的。
数据表行存储格式
我们可以使用show table命令来查看当前表使用的行格式。
查看当前表使用的行格式
从以上字段可以看出该表采用行格式。
事实上,目前的MySQL数据表一般都采用行记录格式。
我们来看看下行链路的格式是什么样的。
行记录格式
该格式将行记录分为两部分:行记录的附加信息和行记录的实际数据。
该行记录的附加信息:
该行记录的实际数据:
它包含了一行中每一列的实际内容。除了我们创建表时涉及到的列之外,还有一些隐藏的列。
比如这是一个隐藏的主键,当没有主键时,表创建时会自动生成。还有一些字段用于记录哪个事务修改了当前行数据,还有一个字段用于指向当前行数据的前一个版本,通过这个字段可以为这行数据形成一个版本链,从而实现多版本并发控制(MVCC)。是不是很眼熟?这个在我之前的帖子里出现过。
隐藏列是什么?
那么我们回顾一下我们创建的表。当只有一个非空字段时,行记录如下所示。
单个数据条目的行记录格式。
前面说过,最大行大小65535字节不包括隐藏列和记录头信息,所以实际上指的是上图中红色的部分。
最左边的变长字段长度列表中,用两个字节来表示该列的长度,共16位,能表示的最大长度为65535,刚好够表示该列的当前长度,即65533。
所以 65535 - 65533 = 2。2 的差值用于存储字段的长度。
一个页面只有16k,如何保存65533(64k)个数据?
在以前的文章中我多次提到过,MySQL在底层是以页(page)的形式存储数据的,并且一个页固定为16KB,而一个字段最多可以容纳65533字节的数据,也就是约64KB,整整4个16KB的页。
页面结构
这是如何实现的?
这种情况下,行数据只保存了这个大字段的一个20字节的指针(其实是偏移量)。这个指针指向一个新的页面(off page),里面保存了该字段实际的65533字节数据。这种由于字段过长而需要额外一个页面来存储数据的现象称为行溢出。
行溢出
如何处理大于 64k 的字符串?
如果数据量大于64k,就不能再使用了,需要改用text或者blob类型的字段。
text和blob类型本身又分为三个级别:TINY、LONG,对应不同的数据长度,最大可达4G左右。
数据类型可以定义如下。
CREATE TABLE `test_max_length` (
`test` LONGTEXT NOT NULL COMMENT '测试长度字段'
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;它们的存储方式与类似,只存储20个字节的指针,实际数据都存储在其他溢出页中。
以前我们查找一行数据的时候,它们都在一个16k的数据页里,查询的时候只需要一次磁盘IO就可以把这个数据页读出来。
当数据库中某一行数据包含特别大的字符串的时候,我们如果要读取整行数据,就得把页外数据全部读取过来,这就意味着需要更多的磁盘IO,性能就更加差了。
为了避免这个问题,我们在写SQL的时候,如果发现某个列字段是特别长的字符串,能避免读到的话就尽量不添加,这也是不建议使用* from table的原因。
blob 和文本之间的区别
一般来说,blob和text都可以用来存储很长的字符串,但是它们稍有不同。
我们知道,在字符集()下还有一个校对规则()的概念。比如大写A和小写a能不能算作同一个字符?这个会影响比较和排序,就是用来定义这个规则的。
Blob没有字符集的概念,而text有。这意味着如果使用blob存储text,则不能使用字符集的排序规则进行排序和比较。
还有一点不同,blob 还可以存储二进制数据,比如压缩的文本数据、图片或者视频。大家别笑,虽然不太恰当,但我见过有人用它来存视频……
总结
参考
《MySQL技术内幕》
从根源上理解 MySQL
最近有很多朋友向我询问一些程序员必备的资料,于是我挖出了我藏在箱子底部的宝藏,免费分享给大家!
扫描海报上的二维码即可免费获取。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。