“给用户名字段用 VARCHAR(50) 还是 VARCHAR(200)?反正都是变长的,开大点没关系吧?” “这个状态字段只有几个数字,用 INT(1) 是不是比 INT(10) 更省空间?”
这些问题,是否也曾在你的脑海中盘旋?在数据库设计中,VARCHAR 和 INT 类型后面的数字 (N) 和 (M),像一对神秘的孪生兄弟,看似相似,实则天差地别。
目录
一. 核心谜题:VARCHAR(N) vs INT(M),数字代表什么?
VARCHAR(10) vs VARCHAR(100):空间占用对比
一. 核心谜题:VARCHAR(N) vs INT(M),数字代表什么?
-
VARCHAR(N)中的N代表的是最大可存储的字符数。它定义了该字段的上限,告诉 MySQL:“这个字段最多能放 N 个字符,多了就报错!” -
INT(M)中的M代表的是显示宽度 (Display Width)。它是一个纯粹的、与存储无关的元数据,仅在与ZEROFILL属性配合使用时,才在客户端显示结果时起到左侧补零、凑足 M 位宽度的作用。它不影响存储空间,也不限制存入值的范围。
二. VARCHAR(N):N 是上限,而非长度
VARCHAR 是 "Variable-length Character String" 的缩写,意为“可变长度字符串”。
底层存储原理:变长的艺术
无论是在 MyISAM 还是 InnoDB 中,VARCHAR 类型的实际存储空间都由两部分组成:
-
内容本身 (Content):你实际存入的字符串数据。
-
长度前缀 (Length Prefix):一个或两个字节,用于记录内容的实际长度。
这个“长度前缀”是实现变长的关键。当 MySQL 读取 VARCHAR 数据时,它会先读取这个前缀,得知后面紧跟着的内容有多长,然后再精确地读取相应长度的数据。
-
如果列的最大长度
N小于等于 255 个字符,则使用 1 个字节来存储长度。 -
如果列的最大长度
N大于 255 个字符,则使用 2 个字节来存储长度。
注意: 这里的长度单位取决于表的字符集。对于 utf8mb4,一个字符最多可能占用4个字节。
VARCHAR(10) vs VARCHAR(100):空间占用对比
现在,我们来解答那个经典问题。假设我们有一张使用 utf8mb4 字符集的表,里面有两个字段:v10 VARCHAR(10) 和 v100 VARCHAR(100)。
当我们同时向这两个字段存入字符串 'hello' 时:
-
v10(VARCHAR(10)) 的存储:-
内容
'hello'包含 5 个字符。 -
长度为 5,小于 255,所以长度前缀占用 1 个字节。
-
总占用空间 = 1 (长度) + 5 * (字符平均字节数) = 1 + 5 = 6 字节 (假设是纯英文字符)。
-
-
v100(VARCHAR(100)) 的存储:-
内容
'hello'包含 5 个字符。 -
长度为 5,小于 255,所以长度前缀占用 1 个字节。
-
总占用空间 = 1 (长度) + 5 * (字符平均字节数) = 1 + 5 = 6 字节。
-
惊人的结论: 当存储相同内容时,VARCHAR(10) 和 VARCHAR(100) 占用的物理空间是完全相同的!N 的大小只决定了“天花板”有多高,而不决定你脚下的“地板”有多厚。
只有当 N 从小于等于255跨越到大于255时,长度前缀才会从1字节变为2字节,导致微小的存储差异。例如,VARCHAR(255) 和 VARCHAR(256) 在存储相同短字符串时,后者会多占用1个字节的长度前缀。
存储结构图解

从上图可见,只要最大长度N不超过255,存储相同内容时,其结构完全一致。当N超过255(如VARCHAR(300)),长度前缀会变为2字节。
三. INT(M):M 是宽度,而非容量
INT 或 INTEGER 是定长整数类型。
底层存储原理:固定的力量
与 VARCHAR 不同,INT 家族(TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT)都是定长 (Fixed-length) 类型。这意味着,无论你存入的数字是 1 还是 10000,它占用的物理存储空间都是固定不变的。
-
TINYINT: 1 字节 -
SMALLINT: 2 字节 -
MEDIUMINT: 3 字节 -
INT: 4 字节 -
BIGINT: 8 字节
INT(M) 中的 M 与存储完全无关。它只在一种情况下起作用:当该列同时设置了 ZEROFILL 属性时。
例如,一个字段定义为 id INT(5) ZEROFILL:
-
当你存入
123,查询时会显示00123。 -
当你存入
123456,它不会被截断,而是会完整显示123456。M不会限制值的范围。
INT(1) vs INT(10):空间占用对比
现在我们来分析 INT(1) 和 INT(10)。
-
INT(1):它是一个标准的INT类型,占用 4 个字节。 -
INT(10):它也是一个标准的INT类型,同样占用 4 个字节。
结论: INT(1) 和 INT(10) 占用的存储空间永远是相同的,都是 4 个字节。M 的值对存储空间没有任何影响。将 INT 的 M 值设为 1,并不能帮你节省任何空间。如果你想节省空间,应该选择更小的整数类型,如 TINYINT。
存储结构图解

上图清晰地展示了,无论是 INT(1) 还是 INT(10),无论是存储小数字还是大数字,它们都固定占用 4 个字节的存储空间。
四. 终极对决:空间占用与性能考量
空间占用总结表
| 类型 | 存储内容 | 占用空间 (假设纯英文) | 解释 |
|---|---|---|---|
| VARCHAR(10) |
| 1 (长度) + 4 (内容) = 5 字节 | 变长,按需分配 |
| VARCHAR(100) |
| 1 (长度) + 4 (内容) = 5 字节 | 变长,与 |
| INT(1) |
| 4 字节 | 定长,与 |
| INT(10) |
| 4 字节 | 定长,与 |
性能影响:变长与定长的博弈
虽然在存储相同短内容时,VARCHAR(10) 和 VARCHAR(100) 空间占用一样,但在性能上,选择一个更精确的 N 仍然是有意义的:
-
内存消耗:当 MySQL 在内存中(例如,为了排序或创建临时表)处理数据时,它可能会为
VARCHAR分配N定义的最大长度。一个过大的N(如VARCHAR(2000)) 可能会导致在内存中分配不必要的空间,尤其是在使用MEMORY存储引擎的临时表时。 -
索引效率:索引对字段的长度有限制。虽然
VARCHAR的索引长度可以自定义,但一个更短的列定义从根本上说更有利于索引效率。 -
数据验证:
N是最后一道数据完整性防线,一个合理的N可以防止异常的超长数据被插入。
而定长类型(如 INT)在处理上通常更简单、更快速。因为不需要计算长度,CPU 在处理定长字段时效率更高,行在内存中的定位也更直接。
五. 设计准则与最佳实践
-
为
VARCHAR选择合理的N:遵循“最小化”原则。评估你的业务场景,为字段选择一个既能满足当前和可预见的未来需求,又不过于宽泛的N值。例如,用户名可以设为VARCHAR(50),而不是VARCHAR(255)。 -
正确选择
INT家族:不要再纠结于INT(M)的M。根据你的数值范围选择最合适的类型。-
用户状态(0-10):
TINYINT(1字节) 足够。 -
文章点赞数:
INT(4字节) 或BIGINT(8字节) 可能更合适。 -
主键 ID:
INT UNSIGNED或BIGINT UNSIGNED是常用选择。
-
-
忘记
ZEROFILL:ZEROFILL是一个很少使用的遗留特性,它会自动为列添加UNSIGNED属性。在应用层处理格式化通常是更好的选择。除非有特殊需求,否则可以忽略INT后面的(M)。 -
VARCHARvsCHAR:如果一个字符串字段的长度是固定的(如 MD5 摘要、UUID),使用定长的CHAR类型通常比VARCHAR性能更好。
通过遵循这些准则,你不仅能设计出存储高效的数据库,还能在性能上抢占先机。
六. 附录:图表
VARCHAR 存储结构图 :

INT 存储结构图 :

1234

被折叠的 条评论
为什么被折叠?



