MySQL中的varchar(N)

本文详细解析了MySQL中VARCHAR类型的使用特点,包括不同版本下VARCHAR的定义变化、存储限制、编码限制及长度限制等内容,并通过具体测试案例展示了不同字符集下VARCHAR的实际表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 http://blog.itpub.net/22664653/viewspace-1979335/

理论知识

 先说明一下 MySQL 历来版本对 varchar 的定义: 
   4.0版本以下,varchar(50),指的是50字节,如果存放UTF8汉字时,只能存16个(每个中文3字节)
   5.0版本以上,varchar(50),指的是50字符,无论存放的是数字、字母还是UTF8中文(每个中文3字节),都可以存放50个 
 存储限制
   需要额外占用字节存放字符的长度:小于255为1个字节,大于255则要2个字节
 编码限制
   gbk :每个字符最多占用2个字节
   utf8:每个字符最多占用3个字节
   utf8mb4 每个字符最多占用4个字节,中文占3个字节,emoji表情符号 占用4个字节 
 长度限制
   MySQL定义行的长度不能超过65535,该数值限制了列的数目,比如char(128) utf8字符集,最多有65535/(128*3)=170个汉字。

三 测试
环境 Server version: 5.6.26-74.0-log Percona Server  
mysql> create table  t1
    -> (id int  NOT NULL AUTO_INCREMENT    primary key,
    ->  name  varchar(10)
    -> ) engine=innodb default charset=utf8mb4;
Query OK, 0 rows affected (0.01 sec)
mysql> create table  t2
    -> (id int  NOT NULL AUTO_INCREMENT    primary key,
    ->  name  varchar(10)
    -> ) engine=innodb default charset=utf8;
Query OK, 0 rows affected (0.01 sec)
mysql> create table t3
    -> (id int  NOT NULL AUTO_INCREMENT    primary key,
    ->  name  varchar(10)
    -> ) engine=innodb default charset=gbk;
Query OK, 0 rows affected (0.01 sec)
utf8mb4 字符集
mysql> insert into t1(name) values('abcdfeghi');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1(name) values('abcdfeghij');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1(name) values('abcdfeghijk');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into t1(name) values('一二三四五六七八九十');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1(name) values('一二三四五六七八九十一');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into t1(name) values('0123456789');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1(name) values('01234567890');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select id,name,length(name),char_length(name) from t1;
+----+--------------------------------+--------------+-------------------+
| id | name                           | length(name) | char_length(name) |
+----+--------------------------------+--------------+-------------------+
|  1 | abcdfeghi                      |            9 |                 9 |
|  2 | abcdfeghij                     |           10 |                10 |
|  3 | abcdfeghij                     |           10 |                10 |
|  4 | 一二三四五六七八九十              |           30 |                10 |
|  5 | 一二三四五六七八九十              |           30 |                10 |
|  6 | 0123456789                     |           10 |                10 |
|  7 | 0123456789                     |           10 |                10 |
+----+--------------------------------+--------------+-------------------+
7 rows in set (0.00 sec)
utf8 字符集
mysql> insert into t2(name) values('abcdfeghi');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2(name) values('abcdfeghij');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2(name) values('abcdfeghijk');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into t2(name) values('一二三四五六七八九十');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2(name) values('一二三四五六七八九十一');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into t2(name) values('0123456789');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2(name) values('01234567890');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select id,name,length(name),char_length(name) from t2;
+----+--------------------------------+--------------+-------------------+
| id | name                           | length(name) | char_length(name) |
+----+--------------------------------+--------------+-------------------+
|  1 | abcdfeghi                      |            9 |                 9 |
|  2 | abcdfeghij                     |           10 |                10 |
|  3 | abcdfeghij                     |           10 |                10 |
|  4 | 一二三四五六七八九十              |           30 |                10 |
|  5 | 一二三四五六七八九十              |           30 |                10 |
|  6 | 0123456789                     |           10 |                10 |
|  7 | 0123456789                     |           10 |                10 |
+----+--------------------------------+--------------+-------------------+
7 rows in set (0.00 sec)
gbk 字符集
mysql> insert into t3(name) values('abcdfeghi');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3(name) values('abcdfeghij');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3(name) values('abcdfeghijk');
Query OK, 1 row affected, 1 warning (0.00 sec) 
mysql> insert into t3(name) values('一二三四五六七八九十');
Query OK, 1 row affected (0.01 sec)
mysql> insert into t3(name) values('一二三四五六七八九十一');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into t3(name) values('0123456789');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3(name) values('01234567890');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select id,name,length(name),char_length(name) from t3;
+----+--------------------------------+--------------+-------------------+
| id | name                           | length(name) | char_length(name) |
+----+--------------------------------+--------------+-------------------+
|  1 | abcdfeghi                      |            9 |                 9 |
|  2 | abcdfeghij                     |           10 |                10 |
|  3 | abcdfeghij                     |           10 |                10 |
|  4 | 一二三四五六七八九十              |           20 |                10 |
|  5 | 一二三四五六七八九十              |           20 |                10 |
|  6 | 一二三四五六七八九十              |           20 |                10 |
|  7 | 0123456789                     |           10 |                10 |
|  8 | 0123456789                     |           10 |                10 |
+----+--------------------------------+--------------+-------------------+
8 rows in set (0.00 sec)
   从上面的测试可以看出,目前版本中 varchar(N) 定义的长度的单位是字符,length(str)表示str占用的字节数,char_length(str)表示str占用的字符数。
不论什么字符集,对于数字和英文字母都是只占用1个字符,也占用一个字节。而中文汉字因字符集不同而不同。

四 总结
   回过头来回答文章开头的问题varchar(N)可以存放多少个中文汉字。答案是在 5.0 以后的版本中 varchar(N) 可以存放N个汉字 。
   抛开字符集 ,如果一行数据全部为 varchar 类型,其最大长度为 65535 个字节。
行长度计算公式如下:
row length = 1
           + (sum of column lengths)
           + (number of NULL columns + delete_flag + 7)/8
           + (number of variable-length columns)
 对于MyISAM,需要额外1个位来记录值是否为NULL;对于InnoDB,没有区别
 对于row_format为fixed,delete_flag为1;对于row_format=dynamic,delete_flag为0

根据这个公式,我们便能够解答开头N的最大值:(65535-1-2)/3
减1是因为实际存储从第2个字节开始
减2则因为要在列表长度存储实际字符长度
除3是因为utf8编码限制
再来一道:
create table t4(c int, c2 char(30), c3 varchar(N)) charset=utf8;
N的最大值:(65535-1-2-4-30*3)/3
则此处N的最大值为 (65535-1-2-4-30*3)/3=21812
减1和减2与上例相同;
减4的原因是int类型的c占4个字节;
减30*3的原因是char(30)占用90个字节,编码是utf8。
如果被varchar超过上述的b规则,被强转成text类型,则每个字段占用定义长度为11字节,当然这已经不是“varchar”了
<think>首先,用户的问题是:“我想了解MySQLVARCHAR类型是基于字符长度还是字节长度进行存储 请问MySQL VARCHAR类型是按字符长度还是字节长度定义”。这是一个关于MySQL数据库的VARCHAR类型定义的问题。 我需要参考提供的引用内容来回答这个问题。引用内容如下: - 引用[1]:在MySQL 5.X以上的版本中,VARCHAR(20)表示字符长度,可以存储20个英文字符或20个中文字符。在MySQL 4.X版本中,表示字节长度。 - 引用[2]:VARCHAR类型存储变长字段的字符类型,存储时需要前缀长度列表。如果字符串长度小于255字节,需要1字节空间;如果大于255字节,需要2字节空间。 - 引用[3]:在MySQL 5.0以上版本,VARCHAR的最大长度是65535字节,但受限于最大行长度。65535字节包括所有字段的长度、变长字段的长度标识和NULL标志位。 - 引用[4]:在UTF8MB4字符集下,一个中文汉字占3个字节。例如,VARCHAR(60)存储“万科”,实际长度是6字节。 - 引用[5]:在Oracle中,可以用字符为单位定义VARCHAR2字段,但MySQL类似。 从这些引用中,我可以总结出: - 在MySQL中,VARCHAR类型定义的长度指的是字符长度,而不是字节长度。具体来说: - MySQL 5.X及以上版本:VARCHAR(n) 中的n表示最大字符数。 - MySQL 4.X版本:表示字节长度。 - 实际存储时,VARCHAR是变长的,存储的字节数取决于字符集和实际内容。例如,在UTF8MB4字符集下,一个中文字符占用3个字节,所以VARCHAR(10)可以存储10个字符,但每个字符可能占用多个字节。 用户的问题直接问“是按字符长度还是字节长度定义”,所以我需要明确回答:在MySQL中,VARCHAR类型定义的长度是基于字符长度的(在MySQL 5.X及以上版本)。 现在,回答结构需要清晰,帮助用户逐步解决问题。系统指令要求: - 回答结构清晰。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 所有行内数学表达式用$...$,独立公式用$$...$$,但这个问题可能不需要数学表达式,除非涉及计算。 - 保证回答真实可靠。 所以,回答结构: 1. **直接回答问题**:明确说明VARCHAR类型定义的长度是字符长度(在MySQL 5.X及以上)。 2. **解释细节**:描述不同MySQL版本的区别,以及存储机制。 3. **引用支持**:在解释时,引用提供的引用内容。 4. **生成相关问题**:在最后,添加相关问题部分。 最后,添加相关问题。系统指令说“生成相关问题”,并给出示例格式。 相关问题应该基于用户的问题,比如: - VARCHAR的最大长度限制是什么? - 不同字符集如何影响VARCHAR存储? - 如何计算VARCHAR字段的实际存储大小? - MySQL 4.X和5.X的差异? 从引用中,我可以提取一些点。 完整回答结构: - 开头:直接回答。 - 主体:详细解释,分步说明。 - 结尾:相关问题。 添加引用标识:例如,在提到MySQL 5.X时,加[^1]。 现在,草拟回答: 回答: 在MySQL中,VARCHAR类型定义的长度是基于字符长度的,而不是字节长度。具体来说: - 在MySQL 5.X及以上版本中,VARCHAR(n) 中的n表示最大字符数。例如,VARCHAR(10)可以存储最多10个字符,无论这些字符是英文还是中文[^1]。 - 在MySQL 4.X版本中,VARCHAR(n) 表示字节长度,存储时会根据字符集计算字节数[^1]。 实际存储时,VARCHAR是变长字符串类型。存储的字节数取决于字符集和实际内容。例如: - 在UTF8MB4字符集下,一个中文字符通常占用3个字节[^4]。 - 存储机制包括前缀长度标识:如果字符串长度小于255字节,需要1字节的前缀;如果大于255字节,需要2字节的前缀[^2]。 此外,VARCHAR的最大长度受限于最大行长度(65535字节),但实际可定义的长度会因字符集和行中其他字段而不同[^3]。 总之,在大多数现代MySQL版本(5.X及以上),VARCHAR(n) 定义的是字符长度,确保存储时能容纳n个字符。 相关问题: 1. 在MySQL中,如何计算VARCHAR字段的实际存储大小? 2. VARCHAR和CHAR类型在存储上有什么区别? 3. 不同字符集(如UTF8MB4 vs LATIN1)如何影响VARCHAR的存储空间? 4. 在MySQL 4.X版本中,VARCHAR的定义方式有什么不同? 确保引用正确: - 对于MySQL 5.X版本字符长度:[^1] - 对于存储机制:[^2]和[^3] - 对于字符集影响:[^4] 在回答中自然添加引用。</think>### MySQLVARCHAR类型定义的长度解析 在MySQL中,VARCHAR类型定义的长度**基于字符长度(character length)**,而非字节长度(byte length)。以下是详细解释,帮助您逐步理解: 1. **MySQL版本差异**: - **MySQL 5.X及以上版本**:VARCHAR(n) 中的 `n` 表示最大字符数(character count)。例如,`VARCHAR(10)` 可以存储最多10个英文字符或10个中文字符(无论字符占用多少字节),定义时以字符为单位[^1]。 - **MySQL 4.X版本**:VARCHAR(n) 中的 `n` 表示字节长度(byte length),存储时会根据字符集动态计算字节数[^1]。但由于MySQL 4.X已过时,现代应用通常使用5.X及以上版本,因此字符长度是主流定义方式。 2. **存储机制**: - VARCHAR是变长字符串类型,实际存储的字节数取决于字符集(character set)和具体内容。例如: - 在UTF8MB4字符集下,一个英文字符占用1字节,一个中文字符占用3字节[^4]。 - 存储时需添加前缀长度标识:如果字符串长度小于255字节,前缀占用1字节;如果大于255字节,前缀占用2字节[^2]。 例如,定义 `VARCHAR(60)` 并存储 "万科"(2个中文字符),在UTF8MB4下实际字节长度为6字节(2字符 × 3字节/字符),但定义的长度60表示最多可容纳60个字符[^4]。 3. **最大长度限制**: - VARCHAR的最大理论长度为65535字节(包括所有字段长度、变长字段标识和NULL标志位),但实际可定义的最大字符数受字符集影响。例如: - 在LATIN1字符集(单字节字符)下,`VARCHAR(65535)` 可能有效。 - 在UTF8MB4字符集(多字节字符)下,最大字符数约为65535/3 ≈ 21845字符(需考虑行大小限制)[^3]。 实际使用中,需通过 `SHOW TABLE STATUS` 或计算行大小来验证。 4. **与字节长度的关系**: - 虽然定义基于字符长度,但实际存储占用字节数需通过函数如 `LENGTH()`(字节长度)和 `CHAR_LENGTH()`(字符长度)计算。例如: ```sql SELECT name, CHAR_LENGTH(name) AS char_len, LENGTH(name) AS byte_len FROM users; ``` 在UTF8MB4下,若 `name VARCHAR(10)` 存储 "Hello"(5字符),则 `char_len=5`,`byte_len=5`;若存储 "你好"(2字符),则 `byte_len=6`[^4]。 **总结**:在MySQL 5.X及以上版本中,VARCHAR(n) 的 `n` 明确表示字符长度,确保用户定义时无需考虑字节转换。存储引擎会根据字符集自动处理字节级细节,但需注意字符集选择对实际存储空间的影响(如UTF8MB4比LATIN1占用更多字节)[^1][^2][^3][^4]。 ### 相关问题 1. 如何计算MySQLVARCHAR字段的实际存储大小(包括前缀和字符集影响)? 2. VARCHAR和CHAR类型在存储机制和性能上有何区别? 3. 不同字符集(如UTF8MB4、LATIN1)如何影响VARCHAR的最大可定义长度? 4. 在MySQL中,如何优化VARCHAR字段以减少存储空间占用?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值