MySQL中的varchar
mysql的varchar需要从InnoDB的行格式说起
B 树节点中的索引记录 可以理解B树上存储数据的一个数据页
InnoDB 行格式
InnoDB 目前有四种行格式: Redundant(冗余)、Compact(紧凑)、Dynamic(动态)和 Compressed(压缩)
Redundant(冗余) 已经是过时的, MySQL 5.0 版本之前用的行格式,现在基本没人用了。
这里着重介绍Compact(紧凑)格式。
Dynamic(动态) 格式与Compact(紧凑)基本一致,不同的地方在于可变字段的存储处理。
长度大于或等于 768 字节的固定长度列被编码为可变长度列
-
COMPACT行格式将可变长度列值(VARCHAR、VARBINARY、BLOB和TEXT类型)的前 768 字节存储在 B 树节点中的索引记录中,其余部分存储在溢出页上。 -
Dynamic(动态) 格式则是将某一列字段完全存储在同一页中。列是否存储在页外取决于页大小和行的总大小。当一行数据过长时,会依次选择最长的列进行页外存储,直到该数据页能存下该记录。小于或等于 40 字节的
TEXT和BLOB列会直接存储在行内。
记录的额外信息
记录的额外信息包含三个部分:变长字段列表、NULL值列表、记录头信息
- 变长字段列表: 这部分就是用来存储varchar的真实长度的。此外,TEXT、BLOB等长度不固定的字段长度也是在此处存储。
- NULL值列表:当某列的值为NULL时,并不会在绿色区域存储该字段,而是会在紫色NULL值列表用一个标志位表示该字段为NULL。
- 二进制位的值为
1时,代表该列的值为NULL。 - 二进制位的值为
0时,代表该列的值不为NULL。
- 二进制位的值为
- 记录头信息:这里边包含的内容比较多,此处不展开。
Varchar(n)
字节和字符
首先要强调的是,这里的varchar(n)中的n指的是字符数。可变长度列表中存储的是列所占用的字节数。
字符数与字节数存在一定的映射关系,这取决于不同的字符集,例如utf8mb4中汉字一般占3个字节、一些特殊的表情符号(如 😀、👍)占4个字节。
可变长度列表中会用1或2个字节存储列的字节数。
- 1个字节8位,最大可以表示的数值是255,即当时,用1个字节存储长度。
- 2个字节16位,最大可以表示的数值是65535,即时,用2个字节存储长度。
n的取值
| 1字节存储长度 | 2字节存储长度 | |
|---|---|---|
| 1 字节字符 | 1~255 | 256 ~ 65535 |
| 2 字节字符 | 1~127 | 128 ~ 32767 |
| 3 字节字符 | 1~85 | 86~21845 |
| 4 字节字符 | 1~63 | 64~16383 |
如上表,以3字节字符(一个字符占用3个字节的存储空间)为例,假设存储的数据均是3字节字符,varchar(1)、varchar(2)…varchar(85)存储长度额外占用的空间都是1字节。varchar(86)、varchar(87)…varchar(21845)存储长度额外占用的空间都是2字节。
因此,除非业务对数据长度有严格要求,否则n的取值也就没有必要纠结了。推荐:1字节存储长度能满足业务需求时,n直接取对应上限即可。超过1字节存储长度,时够用然后稍稍冗余一些。
从上表也可以看出,n的最大取值是65535。但需要注意的是,MySQL行大小限制65535字节,还有记录头、隐藏字段等,即便是存储的都是1字符字节,也无法存储65535个。
数据字符数超过了n怎么办
当采用不同的Server SQL模式时,会有不同的处理方式
-
启用严格 SQL 模式时,
CHAR或VARCHAR列超过列的最大长度,则该值将被截断,并生成警告。 -
未启用严格 SQL 模式时,首先会对结尾的空格字符进行截断,截断后如果仍然超过n,则会发生错误(而不是警告)并禁止插入值。
n存储在哪里
当数据字符数超过n时会有对应的处理,那么是从哪里取出n值进行判断的呢,n值又存储在了哪里?
在MySQL系统数据库information_schema中有一个COLUMNS表,该表中记录了数据库中所有表的列(字段)的详细信息。通过查询 COLUMNS 表,可以获取列的元数据,如数据类型、是否允许空值、默认值等。
| 字段名 | 数据类型 | 描述 |
|---|---|---|
TABLE_SCHEMA | varchar(64) | 列所属的数据库名称(即数据库名)。 |
TABLE_NAME | varchar(64) | 列所属的表名。 |
COLUMN_NAME | varchar(64) | 列的名称(字段名)。 |
ORDINAL_POSITION | int | 列在表中的顺序位置(从 1 开始编号)。 |
COLUMN_DEFAULT | text | 列的默认值(如果未显式设置则为 NULL)。 |
IS_NULLABLE | varchar(3) | 列是否允许 NULL 值(值为 YES 或 NO)。 |
DATA_TYPE | varchar(64) | 列的数据类型(如 int、varchar、datetime 等,不包含长度或精度)。 |
CHARACTER_MAXIMUM_LENGTH | int | 字符类型列的最大长度(如 varchar(255) 中的 255,非字符类型为 NULL)。 |
NUMERIC_PRECISION | int | 数字类型列的精度(如 DECIMAL(10,2) 中的 10)。 |
NUMERIC_SCALE | int | 数字类型列的小数位数(如 DECIMAL(10,2) 中的 2)。 |
DATETIME_PRECISION | int | 时间类型列的精度(如 DATETIME(6) 中的 6,表示微秒)。 |
CHARACTER_SET_NAME | varchar(32) | 列的字符集名称(如 utf8mb4)。 |
COLLATION_NAME | varchar(32) | 列的排序规则(如 utf8mb4_general_ci)。 |
COLUMN_TYPE | text | 列的完整数据类型定义(如 varchar(255)、int(11))。 |
COLUMN_KEY | varchar(3) | 列是否属于索引:PRI(主键)、UNI(唯一键)、MUL(普通索引)。 |
EXTRA | varchar(30) | 附加信息(如 auto_increment、VIRTUAL GENERATED 等)。 |
COLUMN_COMMENT | varchar(1024) | 列的注释(通过 COMMENT 定义的描述信息)。 |