在MySQL建表时,几乎每个开发人员都会用到VARCHAR类型,但真正理解括号里那个数字含义的人并不多。有人认为VARCHAR(255)表示最多存储255个字节,有人认为它和CHAR一样是定长分配,还有人担心VARCHAR(100)会浪费空间。这些理解偏差可能导致字段长度设计不合理、索引创建失败等实际问题。本文将围绕VARCHAR长度的定义、存储机制、限制规则和设计建议展开详细讲解。

一、VARCHAR长度指的是字符数而不是字节数
MySQL官方文档中明确定义:VARCHAR(M)中的M表示该列最多可以存储M个字符,而不是字节。这一点在MySQL 4.1之后的版本中都是如此,之前的旧版本才按字节计算。字符和字节的区别取决于字符集:
-- 在不同字符集下创建同样的VARCHAR(10)
CREATE TABLE t_latin1 (
name VARCHAR(10)
) CHARACTER SET latin1;
CREATE TABLE t_utf8mb4 (
name VARCHAR(10)
) CHARACTER SET utf8mb4;
-- latin1下name最多存10个字符,最大占用10字节
-- utf8mb4下name最多存10个字符,最大占用40字节从上面的例子可以看出,latin1字符集中一个字符占1个字节,所以VARCHAR(10)最多占10字节;而utf8mb4字符集中一个英文字母占1字节、一个常用汉字占3字节、一个emoji表情占4字节,所以VARCHAR(10)最多可能占40字节。这就带来一个很关键的结论:同样的长度声明,在不同字符集下实际占用的字节数完全不同。
还需要说明的是,VARCHAR是变长类型。如果你声明了VARCHAR(100),实际只存了"abc"三个字符,那么磁盘上只占用"abc"本身的存储空间加上1到2个字节的长度前缀,不会因为声明了100就分配100个字符的空间。这也是VARCHAR和CHAR的本质区别:CHAR是定长的,不足长度会用空格补齐;VARCHAR是变长的,存多少占多少。
二、VARCHAR的最大长度限制与长度前缀
VARCHAR(M)中M的理论上限是65535,这是MySQL一行数据最大长度(65535字节)决定的。但实际能声明的最大值要受两个因素制约:字符集的单字符最大字节数,以及一行中其他列占用的空间。来看一个实际测试:
-- utf8mb4下单字符最多占4字节
-- 65535 / 4 = 16383.75,向下取整为16383
CREATE TABLE t_big (
col VARCHAR(16383)
) CHARACTER SET utf8mb4;
-- 建表成功
CREATE TABLE t_big2 (
col VARCHAR(16384)
) CHARACTER SET utf8mb4;
-- 报错:Row size too large除了存储内容本身,VARCHAR还需要额外的字节来记录实际长度。规则是:当声明的最大字节数不超过255时,用1个字节记录长度;超过255时,用2个字节记录长度。这就是为什么VARCHAR(255)在utf8下恰好是个分水岭——utf8单字符最多3字节,255乘以3等于765字节,仍在1字节长度前缀的范围内。
如果往VARCHAR(10)的字段里插入11个字符会发生什么?这取决于sql_mode的配置。默认的严格模式下,MySQL会直接报错,拒绝插入;如果是在非严格模式下,超出的部分会被截断,只保留前10个字符,并产生一个warning。生产环境建议保持严格模式,避免数据被静默截断造成业务异常。
三、索引长度限制:为什么大家都爱用VARCHAR(255)
很多公司的开发规范里都写着"VARCHAR字段长度不超过255",这背后和MySQL索引的长度限制有关。在MySQL 5.6及之前的版本中,使用InnoDB存储引擎且开启innodb_large_prefix参数之前,单列索引的最大长度是767字节。换算一下:
- latin1字符集:767 / 1 = 767字符
- utf8字符集:767 / 3 = 255字符
- utf8mb4字符集:767 / 4 = 191字符
所以在utf8字符集流行的年代,VARCHAR(255)正好能建一个完整的单列索引,这个习惯一直延续到今天。MySQL 5.7之后默认启用了innodb_large_prefix且使用DYNAMIC行格式,单列索引上限提升到了3072字节,utf8mb4下可以索引768个字符,限制宽松了不少。
如果确实需要对超长字段建索引,可以使用前缀索引,只对字段的前N个字符建索引:
-- 对email字段的前20个字符建索引
ALTER TABLE user ADD INDEX idx_email (email(20));
-- 也可以在业务允许的情况下改用哈希列
-- 新增一列存储CRC32(email)的值,对这一列建索引
ALTER TABLE user ADD COLUMN email_hash INT UNSIGNED,
ADD INDEX idx_email_hash (email_hash);前缀索引的缺点是无法用于覆盖查询,也无法直接用于ORDER BY优化,需要在索引选择性和查询性能之间做权衡。一般来说,前缀长度选择区分度达到全列90%以上就可以接受。
四、如何合理设置VARCHAR长度
第一个建议是按业务实际需求设置,不要无脑写VARCHAR(255)。虽然VARCHAR变长存储不会浪费磁盘空间,但过长的声明仍有两个负面影响:一是内存中的内存临时表和排序缓冲区会按最大长度分配,可能导致内存占用偏高;二是过长的字段不利于后续维护者理解业务约束。比如手机号字段就应该声明VARCHAR(11),性别这类短枚举字段用VARCHAR(2)甚至TINYINT更合适。
第二个建议是注意MySQL会隐式截断超长值。哪怕声明VARCHAR(5),只要sql_mode不是严格模式,插入超长数据也只是警告而非报错,排查起来很隐蔽。第三个建议是高频查询字段尽量控制在索引限制以内,避免依赖前缀索引带来的查询限制。
最后用一张表总结常见场景的推荐长度:
| 字段 | 推荐类型 | 说明 |
|---|---|---|
| 手机号 | VARCHAR(11) | 国内手机号固定11位 |
| 邮箱 | VARCHAR(64)或更长 | 按RFC标准最长可到254字符 |
| 用户名 | VARCHAR(32) | 满足绝大多数业务 |
| URL | VARCHAR(512)或TEXT | 超长时建议直接用TEXT |
总结一下:VARCHAR的长度声明以字符为单位,实际占用字节数由字符集决定;长度超过255时记录长度需要2个字节前缀;索引限制是决定字段长度上限的重要因素。理解这些底层机制后,在设计中就能做到心中有数,既不浪费也不踩坑。
MySQL VARCHAR长度varchar字节数字符集修改时间:2026-09-08 20:08:58