MYSQL 5.7 VARCHAR 类型实验

MYSQL 的VARCHAR 类型字段的最多能存储多少字符?模糊记得 VARCHAR 最多能存65535个字符,真的吗?
理论上,一个字符类型能存的字符数量跟选取的编码字符集和存储长度限制肯定是有关系的,字符编码长度越小,长度上限越大,能存的字符就越多。
OK!我们先用字符编码长度最小的latin1做测试:

[testdb]> create table t5(name varchar(65535)) charset=latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
[testdb]> create table t5(name varchar(65534)) charset=latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
[testdb]> create table t5(name varchar(65533)) charset=latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
[testdb]> create table t5(name varchar(65532)) charset=latin1;
Query OK, 0 rows affected (0.01 sec)

一番折腾下来,我们发现被 Row size 限制了,不过测试结果很明显,使用 latin1字符编码时varchar最多能存 65532ge字符,真的如此吗?
答案是 NO!
这个结论明显经不起推敲,参考文档,VARCHAR存储长度超过255的字符串时,需要使用2个字节的前缀表示存储字符串占用的存储空间长度(字节数)。
(2个字节16bit,2^16-1=65535 这也从从另一个层面解释了65535 字节这个限制)

参考MYSQL 5.7 官档:
Values in VARCHAR columns are variable-length strings. The length can be specified as a value from 0 to 65,535.
The effective maximum length of a VARCHAR is subject to the maximum row size (65,535 bytes, which is shared among all columns) and the character set used.
See Section C.10.4, “Limits on Table Column Count and Row Size”.

In contrast to CHAR, VARCHAR values are stored as a 1-byte or 2-byte length prefix plus data.
The length prefix indicates the number of bytes in the value.
A column uses one length byte if values require no more than 255 bytes, two length bytes if values may require more than 255 bytes.

那么,65535-2 =65533 ,但是 create table t5(name varchar(65533)) charset=latin1 依然执行失败了,why?
因为我们忽略了行格式中的 null 标志位,因为我们的表只定义了一个字段,所以标志位需要占用行的一个字节(关于null标志位这里不延伸)。
将name字段定义字段为not null 即可以关闭null 标志位,继续测试:

[email protected] 17:00: [testdb]> create table t6(name varchar(65534) not null) charset=latin1;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
[email protected] 17:00: [testdb]> create table t6(name varchar(65533) not null) charset=latin1;
Query OK, 0 rows affected (0.01 sec)

OK!测试符合理论!

那么在 utf8mb4 下最多能存多少个字符呢?

首先我们来看下试验环境的字符集和行格式相关设置,MYSQL 版本是5.7.22。数据库默认字符集是 utf8mb4
[testdb]> show variables like ‘%char%‘;
+--------------------------+----------------------------------------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
| character_sets_dir | /opt/mysql/mysql-5.7.22-linux-glibc2.12-x86_64/share/charsets/ |
+--------------------------+----------------------------------------------------------------+
8 rows in set (0.00 sec)

创建一个表,指定字段长度为65535:
[testdb]> create table t3(name varchar(65535) primary key);
ERROR 1074 (42000): Column length too big for column ‘name‘ (max = 16383); use BLOB or TEXT instead

根据以上错误信息提示,字段长度最大值为16383;为什么是16383这个值,而不是其他值?
首先依然是被 65,535这个行长度限制了,我们来看看官档中关于 Row size 的描述。

Row Size Limits
The maximum row size for a given table is determined by several factors:
The internal representation of a MySQL table has a maximum row size limit of 65,535 bytes, even if the storage engine is capable of supporting larger rows.
BLOB and TEXT columns only contribute 9 to 12 bytes toward the row size limit because their contents are stored separately from the rest of the row.

也就是说,即使你的存储引擎支持更大的行长度,但是MYSQL 依然限制 Row size为65535;
BLOB and TEXT 这两种类型字段只占用行存储的9-12个字节,其他的内容分开存储。

其次创建表时没有指定表的字符集,所以默认继承数据库字符集 utf8mb4;
在utf8mb4 编码中,字符的最大编码长度是4,比如中文;
所以为了保证存储的字符串实际存储空间小于65535字节,字符串长度不能大于 floor(65535/4)=16383

但是以16383长度再次创建表格,依然提示错误,why?
[testdb]> create table t3(name varchar(16383) primary key);
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

注意看提示信息!这次不再是提示 Column length too big ,而是 Specified key was too long;
Look 下面的官方描述:
Both DYNAMIC and COMPRESSED row formats support index key prefixes up to 3072 bytes.
This feature is controlled by the innodb_large_prefix configuration option, which is enabled by default.
See the innodb_large_prefix option description for more information.

原来 DYNAMIC and COMPRESSED 行格式默认支持索引长度不能超过3072字节.
而我们的 name是聚集索引,整个字段值作为索引键值,所以索引长度必然超限。
而且它还告诉我们,可通过 innodb_large_prefix这个变量来控制这个特性。

检查下我们的试验环境,行格式刚好是 dynamic :
[testdb]> show variables like ‘%format%‘;
+---------------------------+-------------------+
| Variable_name | Value |
+---------------------------+-------------------+
| binlog_format | ROW |
| date_format | %Y-%m-%d |
| datetime_format | %Y-%m-%d %H:%i:%s |
| default_week_format | 0 |
| innodb_default_row_format | dynamic |
| innodb_file_format | Barracuda |
| innodb_file_format_check | ON |
| innodb_file_format_max | Barracuda |
| time_format | %H:%i:%s |
+---------------------------+-------------------+

3072字节除以 utf8mb4 的最大编码长度4字节,在主键字段上长度上限应该是768,测试如下:
[testdb]> create table t4(name varchar(769) primary key) charset=utf8mb4;
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

[testdb]> create table t4(name varchar(768) primary key) charset=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

不出所料,769长度字段建表失败,768长度字段建表成功。
现在抛开索引长度的限制,再次测试:
[testdb]> create table t41(name varchar(16383) not null) charset=utf8mb4;
Query OK, 0 rows affected (0.02 sec)

建表成功!

基于以上理论和实验:
在utf8 编码字符集中,字符的最大编码长度是3字节,比如中文;所以如果 name作为主键,这个字段字符长度不能超过 3072/3=1024;

[testdb]> create table t3(name varchar(1025) primary key) charset=utf8;
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

[testdb]> create table t3(name varchar(1024) primary key) charset=utf8;
Query OK, 0 rows affected (0.01 sec)

在utf8 编码字符集环境中,如果不使用索引,基于验证上面的理论 65535/3= 21845:
[testdb]> create table t32(name varchar(21845) not null ) charset=utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

建表语句依然报错?因为 "VARCHAR values are stored as a 1-byte or 2-byte length prefix plus data."
存储空间字符串前缀需要占用2个字节,所以创建失败。
[testdb]> create table t32(name varchar(21844) not null ) charset=utf8;
Query OK, 0 rows affected (0.01 sec)

建表成功了!

结论:
在latin1 编码字符集中,VARCHAR 类型字段最多能存储65533 个字符;
在utf8 编码字符集中,VARCHAR 类型字段最多能存储21844 个字符;
在utf8mb4 编码字符集中,VARCHAR 类型字段最多能存储16383 个字符;

以上是关于VARCHAR 类型字段存储字符长度,行长度以及索引长度的限制的一个小试验!
不妥之处欢迎指正!

原文地址:https://www.cnblogs.com/lidx/p/9783713.html

时间: 2024-10-13 05:35:44

MYSQL 5.7 VARCHAR 类型实验的相关文章

关于MySQL中的varchar类型的研究

很多人讲MySQL中varchar类型的数据大小应该设置为偶数,因为一个中文占用两个字节.今天我们来测试一下varchar大小是字符还是字节,使用工具:Navicat: 1.首先创建一个t_test表,里面只有一个字段test,将其设置为varchar(1) 2.向表中添加分别添加两个数据"1"和"天" 3.测试结果:varchar()里面输入的大小代表的是字符,为了验证测试的可靠性,添加两个字符报错图片: 原文地址:https://www.cnblogs.com/

Mysql中把varchar类型的字段转化为tinyint类型的字段

因为之前不知道tinyint类型的用法,所以将一些状态属性字段类型设置成了varchar类型,然后用"是"和"否"来判断状态 后来了解到了tinyint,就想试着用一下,但是要把varchar类型的字段转化为tinyint类型的字段,探索方法如下: 1.将表里面的所有"是"或者"否"改成 0,因为当前字段类型是varchar,所以这一步更改是没有问题的 2.运行下面的SQL语句 ALTER TABLE 表名 MODIFY CO

MySql中的varchar类型

做项目过程中遇到输入数据验证问题.使用MySQL5.5数据库,varchar类型问题. 用该类型存储用户名,宽度设为30. 开始以为只能存储30个字符,15个汉字,测试期间,突然发现可存最多30个汉字,字符也是.于是上网查询资料.参考如下: http://www.cnblogs.com/doit8791/archive/2012/05/28/2522556.html

mysql varchar类型使用心得

自己的一个例子,在存储一个email的需求中.mysql定义的字段,类型:varchar 长度:20 当我插入一个email:[email protected]  发现怎么样都差不进去..后面才发现原来是字段长度20不够存放..下次要注意,当然,字段长度设定也不能过于大,够用即可. 以下为转发: 今天新开始的项目在做数据库设计,发现自己对MySql的varchar类型还不熟悉,故又上网收集资料整理如下. 1.varchar类型的变化 MySQL 数据库的varchar类型在4.1以下的版本中的最

Java学习篇之---Mysql中varchar类型总结

Mysql中varchar类型总结 今天新做一个项目,需要自己进行数据库设计,发现自己对varchar的用法还不是很熟悉,所以查阅资料总结若下: 1.varchar类型的变化 MySQL 数据库的varchar类型在4.1以下的版本中的最大长度限制为255,其数据范围可以是0~255或1~255(根据不同版本数据库来定).在 MySQL5.0以上的版本中,varchar数据类型的长度支持到了65535,也就是说可以存放65532个字节的数据,起始位和结束位占去了3个字 节,也就是说,在4.1或以

mysql - varchar类型与数字的比较和转换

mysql - varchar类型与数字的比较和转换 convert(对象, 目标类型)是mysql中常用的类型转换对象函数.eg: select convert('1.123', decimal(10.4)),结果就是1.1230.对象可以用列名替代. 前两天发现,一个小伙伴之前设计表时把某个表数据类型设计成了varchar,实际用于存储Decimal.我需要用其数据进行过滤筛选 如果文章内容有问题,欢迎评论或与我进行讨论(请注明原因): mail: [email protected] 微信:

mysql中char和varchar类型的区别

区别: char:存储的数据时固定长度的: varchar:存储的数据可变长度 举例: 在数据表中定义两个数据项char(200)和varchar(200),同时存入字符串串"Hello Word", char类型便会用这200多的数据长度存储这十几个字符,简直很汗!: varchar类型会按需索取,只会用这十几个字符需要的空间来存储它,再加上一个位置来存储位置信息,嗯,好多了. 注意: 1.这样看起来varchar比char好多了,char有优点吗? 当然有的!!! char类型因为

吐槽 MySQL数据库jdbc操作,varchar类型占位符问题——单引号造孽

很长时间不写代码动手能力明显下降很多常见的错误还是经常发生,今天吐血了一次. 简单的坑总是要多跳几次才能甘心.很清晰的记得大学的时候在此坑差点闷死,现在又跳进这个坑了,搞了半天终于知道错在哪里. String sqlString = "insert into cryptcontent(content,assistentContent) value('asf','asfd')"; DBHelper.insertQuery(sqlString); 这样操作可以的.因此占位符应该也没问题,前

MySQL中varchar类型在5.0.3后的变化

1.mysql varchar类型变化:mysql 5.0.3 之前: 0--255字节 varchar(20)中的20表示字节数,如果存放urf8编码的话只能放6个汉字. MySQL 5.0.3 之后: 0--65535字节 varchar(20)表示字符数,不管什么编码,既汉字也能放20个.但最多占65532字节(两个字节存放长度,小于255字节用1个字节存放长度) 1.存储限制: varchar 字段是将实际内容单独存储在聚簇索引之外,内容开头用1到2个字节表示实际长度(长度超过255时需