MySQL中的varchar

209 阅读5分钟

MySQL中的varchar

mysql的varchar需要从InnoDB的行格式说起

B 树节点中的索引记录 可以理解B树上存储数据的一个数据页

InnoDB 行格式

InnoDB 目前有四种行格式: Redundant(冗余)Compact(紧凑)Dynamic(动态)Compressed(压缩)

Redundant(冗余) 已经是过时的, MySQL 5.0 版本之前用的行格式,现在基本没人用了。

这里着重介绍Compact(紧凑)格式。

image.png

Dynamic(动态) 格式与Compact(紧凑)基本一致,不同的地方在于可变字段的存储处理。

长度大于或等于 768 字节的固定长度列被编码为可变长度列

  • COMPACT 行格式将可变长度列值( VARCHARVARBINARYBLOBTEXT 类型)的前 768 字节存储在 B 树节点中的索引记录中,其余部分存储在溢出页上。

  • Dynamic(动态) 格式则是将某一列字段完全存储在同一页中。列是否存储在页外取决于页大小和行的总大小。当一行数据过长时,会依次选择最长的列进行页外存储,直到该数据页能存下该记录。小于或等于 40 字节的 TEXTBLOB 列会直接存储在行内。

记录的额外信息

记录的额外信息包含三个部分:变长字段列表、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列长度2551 \le 列长度 \le 255时,用1个字节存储长度。
  • 2个字节16位,最大可以表示的数值是65535,即255<列长度65535255 \lt 列长度 \le 65535时,用2个字节存储长度。

n的取值

1字节存储长度2字节存储长度
1 字节字符1~255256 ~ 65535
2 字节字符1~127128 ~ 32767
3 字节字符1~8586~21845
4 字节字符1~6364~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 模式时,CHARVARCHAR 列超过列的最大长度,则该值将被截断,并生成警告。

  • 未启用严格 SQL 模式时,首先会对结尾的空格字符进行截断,截断后如果仍然超过n,则会发生错误(而不是警告)并禁止插入值。

n存储在哪里

当数据字符数超过n时会有对应的处理,那么是从哪里取出n值进行判断的呢,n值又存储在了哪里?

在MySQL系统数据库information_schema中有一个COLUMNS表,该表中记录了数据库中所有表的列(字段)的详细信息。通过查询 COLUMNS 表,可以获取列的元数据,如数据类型、是否允许空值、默认值等。

字段名数据类型描述
TABLE_SCHEMAvarchar(64)列所属的数据库名称(即数据库名)。
TABLE_NAMEvarchar(64)列所属的表名。
COLUMN_NAMEvarchar(64)列的名称(字段名)。
ORDINAL_POSITIONint列在表中的顺序位置(从 1 开始编号)。
COLUMN_DEFAULTtext列的默认值(如果未显式设置则为 NULL)。
IS_NULLABLEvarchar(3)列是否允许 NULL 值(值为 YESNO)。
DATA_TYPEvarchar(64)列的数据类型(如 intvarchardatetime 等,不包含长度或精度)。
CHARACTER_MAXIMUM_LENGTHint字符类型列的最大长度(如 varchar(255) 中的 255,非字符类型为 NULL)。
NUMERIC_PRECISIONint数字类型列的精度(如 DECIMAL(10,2) 中的 10)。
NUMERIC_SCALEint数字类型列的小数位数(如 DECIMAL(10,2) 中的 2)。
DATETIME_PRECISIONint时间类型列的精度(如 DATETIME(6) 中的 6,表示微秒)。
CHARACTER_SET_NAMEvarchar(32)列的字符集名称(如 utf8mb4)。
COLLATION_NAMEvarchar(32)列的排序规则(如 utf8mb4_general_ci)。
COLUMN_TYPEtext列的完整数据类型定义(如 varchar(255)int(11))。
COLUMN_KEYvarchar(3)列是否属于索引:PRI(主键)、UNI(唯一键)、MUL(普通索引)。
EXTRAvarchar(30)附加信息(如 auto_incrementVIRTUAL GENERATED 等)。
COLUMN_COMMENTvarchar(1024)列的注释(通过 COMMENT 定义的描述信息)。