关于java:char和varchar有哪些区别varchar最大长度是多少

67次阅读

共计 6359 个字符,预计需要花费 16 分钟才能阅读完成。

以我多年教训来看,VARCHAR 的最大长度、字符串类型抉择,用 MySQL 的人中十之七八是不分明的。网上文章滥竽充数,以讹传讹居多。

本文不止介绍了原理,还提供了案例手把手教你本人剖析,彻底解决你的纳闷。

假如有个 VARCHAR(64) CHARSET utf8mb4 列,存储了中国 cn 这个字符串。

那你猜一猜,MySQL 存储时用了多少字节?

  • A:4 Bytes
  • B:5 Bytes
  • C:8 Bytes
  • D:9 Bytes
  • E:10 Bytes
  • F:10.125 Bytes
  • G:11 Bytes
  • H:12 Bytes
  • I:12.125 Bytes
  • K:13 Bytes

正确答案是 F 和 G。

如果您没猜对,那么花 7~10 分钟读完本文,即可破解这一谜题。成长高兴轻轻松松。

文章目录

  • VARCHAR的定义
  • VARCHAR的最大长度
  • 最大行大小
  • 可空列标识位
  • 字符集的单字符最大字节数
  • VARCHAR的长度标识位
  • 样例

本文内容实用于 MySQL 5.5/5.6/5.7/8.x

VARCHAR 的定义

VARCHAR 是变长字符串。

思考其变长原理中有较多因素,在具体合成前,有必要一起重温下官网定义。

为了便于了解,我用 CHAR 定长类型来比照介绍。先看两个小例子:

  • VARCHAR(4),最多存储 4 个字符,有几个字符存储几个。存储字节数 = 数据值的字节和 + 1 字节(长度标识,前面会讲到)
  • CHAR(4),最多存储 4 个字符,有余 4 个尾部用空格填满。存储字节数 = 数据值的字节和 + 补位空格数

概括地说,VARCHAR 和 CHAR 都是 MySQL 的字符串类型,存储多个字符、可设置最大存储的字符数,存储开销都与数据长度、字符集无关。是 MySQL 最罕用的字符串类型。

CHAR 和 VARCHAR 具体比照:

如果开启 PAD_CHAR_TO_FULL_LENGTH 模式,检索时尾部空格不会去除

CHAR 超过 255 字符会报错,提醒应用 TEXT 或 BLOB:

ERROR 1074 (42000): Column length too big for column ''long_char''  (max = 255); use BLOB or TEXT instead

VARCHAR 的最大长度

在 MySQL 官网定义中,罕用的 COMPACT、DYNAMIC 行模式下,最大长度受几个因素影响:

  • 行存储的最大字节数
  • 数据之外的存储开销,官网定义中包含:NULL 标识、长度标识
  • 存储字符的字符集

算法如下:

最大长度 (字符数) = (行存储最大字节数 - NULL 标识列占用字节数 - 长度标识字节数) /  字符集单字符最大字节数。有余数时向下取整。

上面通过逐渐实例验证,演示如何计算出最大长度。

最大行大小

MySQL 行默认最大 65535 字节,是所有列共享的,所以 VARCHAR 的最大值受此限度。

接下来,咱们要创立一个 65536 字节的 VARCHAR,来验证这个边界值。

后面讲过,VARCHAR 申明的长度是指字符数。要换算为 65536 字节,最好一个字符只占一个字节。

所以这里应用了 latin1 字符集(MySQL 默认字符集,不指定即为默认)。

mysql> create table test_varchar_length(v varchar(65536) not null);
ERROR 1074 (42000): Column length too big for column 'v' (max = 65535); use BLOB or TEXT instead

能够看到报错了,提醒咱们行最大长度为 65535 字节。

如果咱们要插入一个非空的 VARCHAR,其最大长度不能超过 65535(行最大值) – 2(长度标识位) = 65533 字节(长度标识位需 2 字节能力示意 2^16=65536 个数字):

/** 测试边界值 65534,确认依然过大;留神这里应用默认字符集 latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65534) not null); 
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

/** 测试边界值 65533,创立胜利,阐明行最大值为 65535 */
mysql> create table test_varchar_length(v varchar(65533) not null); 
Query OK, 0 rows affected (0.02 sec)

/** 查看默认字符集,确认是 latin1,每个字符只占用 1 个字节 */
mysql> show create table test_varchar_length;
+----------------------+------------------------------------------------------------------------------------------------------------+
| Table                | Create Table                                                                                               |
+----------------------+------------------------------------------------------------------------------------------------------------+
| test_varchar_length | CREATE TABLE `test_varchar_length` (`v` varchar(65533) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------------------+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

可空列标识位

在 COMPACT、DYNAMIC 行格局下,行大小除了数据列长度,还包含可空列标识,即 NULL 标识位。

如果有一个列容许为空,则须要 1 bit 来标识,每 8 bits 的标识会组成一个字段,该字段会寄存在每行最开始的地位。

留神,这个标识位不是放在每列,而是每行共享。

假如一张表中存在 N 个可空字段,NULL 标识位须要⌈N / 8 ⌉(向上取整)个字节。此时整行可用于数据存储的空间只有 65535 − ⌈ N / 8 ⌉ 个字节。

Talk is cheep,一起来验证下:

在行大小的例子中,咱们晓得最大可创立 65533 字节长度的非空 VARCHAR 列。当初要创立一个可空列,每行须要 1 bit 的 NULL 标识位、MySQL 会将其组装成 1 byte 的字段寄存,那么咱们应该可创立最大为 65533(最大非空 VARCHAR 列) – 1(NULL 标识列)\= 65532 字节的可空 VARCHAR 列:

/** 删除后面创立的表 */
mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)

/** 测试边界值 65533,确认依然过大;留神这里应用默认字符集 latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65533));
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

/** 测试边界值 65532,创立胜利,阐明可空标识列的确占去了 1 字节;留神这里应用默认字符集 latin1、单字节字符集 */
mysql> create table test_varchar_length(v varchar(65532));
Query OK, 0 rows affected (0.03 sec)

计算 VARCHAR 的最大长度,可空标识位是最容易疏忽的。

字符集的单字符最大字节数

字符集单字符最大字节数不难理解,列举 MySQL 常见的三个字符集:

  • GBK:单字符最大可占用 2 个字节。
  • UTF8:单字符最大可占用 3 个字节。
  • UTF8MB4:单字符最大占 4 个字节。

假如还有 6 字节能够寄存字符,按单字符占用最大字节数来算,能够寄存 3 个 GBK、2 个 UTF8、1 个 UTF8MB4。

VARCHAR 的长度标识位

长度标识位是绝对比较复杂的,网上的介绍错的很多,也容易算错。

其作用是记录数据的字节数。

存储开销是小于 255 只有 1 字节、大于 255 后应用两字节。是因为依照可能的数据大小,分为 0 – 255(28)、256 – 65535(216),刚好对应 1 字节和 2 字节。

但要留神,其计算依据的是字段申明的字符长度、计算可能的字节数,再决定长度标记的字节数。如 VARCHAR(100),字符集为 UTF8,可能的字节数为 300,长度标识则为 2 字节。这是网上介绍错的最多的。

另外长度标记位是底层存储开销,不占用字段申明的字符长度。申明的字符长度的是数据的字符数,数据的字节数与字符集无关。

以 VARCHAR(1)为例,能够存 1 个字符,MySQL 会额定找一个字节寄存长度标识

样例

公式应该都了解了:VARCHAR 的最大长度 =(最大行大小 – NULL 标识列占用字节数 – 长度标识字节数)/ 字符集单字符最大字节数。有余数时向下取整。

接下来通过试验来验证。为了便于了解计算,例子做了一些调整:

  • 不设置可空列、这样能够去掉 NULL 标识列
  • 为了便于体现长度标识位的差距,采纳多个列的模式放大其存在
  • 为了体现按可能字节数计算长度,这里采纳多字节的字符集 GBK

创立一个表,蕴含 2 个非空 VARCHAR(127),每个列存储开销为 127*2(可能的最大字节数, GBK 字符占 2 字节)+ 长度标识位 1 \=255字节:

  • 残余空间为 65535 – 255*2 = 65025 字节
  • 残余空间可寄存一个 VARCHAR(32511) NOT NULL 列(32511*2(GBK 字符占 2 字节)+2(长度标识位占 2 字节)=65024
mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)
/** 测试边界值 32512,确认依然过大 */
mysql> create table test_varchar_length(v1 varchar(127) not null,v2 varchar(127) not null,vm varchar(32512) not null) CHARSET=GBK;    
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

/** 测试边界值 32511,创立胜利,阐明两个长度标识位共占去了 2 字节 */
mysql> create table test_varchar_length(v1 varchar(127) not null,v2 varchar(127) not null,vm varchar(32511) not null) CHARSET=GBK; 
Query OK, 0 rows affected (0.02 sec)

接下来将两个字段调大到 128 字符,每个列的存储为最大字节数 256+ 长度标识位 2 =258 字节

  • 残余空间 65535 – 258*2 = 65019 字节
  • 残余空间可寄存一个 VARCHAR(32508) NOT NULL 列(32508*2(GBK 字符占 2 字节)+2(长度标识位占 2 字节)=65018):
mysql> drop table test_varchar_length;
Query OK, 0 rows affected (0.01 sec)
/** 测试边界值 32509,确认依然过大 */
mysql> create table test_varchar_length(v1 varchar(128) not null,v2 varchar(128) not null,vm varchar(32509) not null) CHARSET=GBK; 
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

/** 测试边界值 32508,创立胜利,阐明两个长度标识位共占去了 4 字节 */
mysql> create table test_varchar_length(v1 varchar(128) not null,v2 varchar(128) not null,vm varchar(32508) not null) CHARSET=GBK; 
Query OK, 0 rows affected (0.02 sec)

祝贺你,能看到这里的人预计不多,保持下来的你曾经失去了晋升。

那么再一起解下最后的问题:

  • UTF8MB4 字符中,中文字符须要 3 个字节(大部分中文只须要 3 字节,4 字节次要是 emoji 等辅助立体字符),那么“中国 cn”须要 3 +3+1+ 1 共 8 个字节
  • VARCHAR(64) CHARSET utf8mb4字段,数据最大可能的字节数是64*4=256,所以须要 2 个字节 作为长度标识位;
  • 该字段是能够为空的,那么还须要 NULL 标识位,MySQL 会生成一个 1 字节 的 NULL 标识列来记录;
  • 所以要存储“中国 cn”,列须要 8 + 2 个字节,还须要 1 字节作为 NULL 标识列;因为该列是多个列共享的,如果该表只有一个字段,那么能够存储开销应该是 11 个字节,否则只能算作 10.125 字节(1/8等于 0.125)

所以答案是 10.125 或 11 字节。

正文完
 0