char适合固定长度、极短且恒定的字符串(如国家代码、性别、md5),定长提升索引排序性能但浪费空间;varchar节省存储但有长度前缀开销及行迁移风险,且尾空格语义严格保留。

CHAR 适合固定长度、短且一致的字符串;VARCHAR 更省空间,但有长度前缀开销和潜在的行迁移风险。
CHAR 存储时自动填充空格,读取时默认去掉尾部空格
比如定义 CHAR(10),插入 'abc',实际存入的是 'abc '(7个空格),但 SELECT 出来是 'abc'。这个行为在绝大多数场景下是透明的,但如果你依赖尾部空格做业务逻辑(比如密码哈希后带空格),就会出问题。
可以通过开启 PAD_CHAR_TO_FULL_LENGTH SQL 模式来禁用自动去空格,但不推荐全局开启——它会影响 ORDER BY、GROUP BY 和索引比较行为。
- 插入
'a '(带一个尾空格)到CHAR(5)→ 存为'a ',读出为'a' - 插入同样内容到
VARCHAR(5)→ 存为'a ',读出仍是'a ' - WHERE 条件中比较
CHAR值时,MySQL 会隐式忽略尾空格,VARCHAR则严格按字节比
VARCHAR 实际占用空间 = 字符长度 + 1 或 2 字节长度前缀
VARCHAR(255) 在 utf8mb4 下最多存 255 个字符,但每个字符最多占 4 字节,所以最大可能占用 255×4 + 2 = 1022 字节(超 255 字节需 2 字节前缀)。而 CHAR(255) 固定占 255×4 = 1020 字节(无前缀)。
关键点在于:当字段平均长度远小于定义长度时,VARCHAR 节省明显;但若大部分数据都接近满长(比如用户昵称普遍 20–25 字符,而你定义了 VARCHAR(255)),那额外的长度前缀和行内碎片反而可能抵消空间优势。
- 短值如
'OK'存进VARCHAR(255)→ 占 2 字符 + 1 字节前缀 = 3 字节 - 同内容存进
CHAR(255)→ 固定占 255×4 = 1020 字节(utf8mb4) - 长度 ≤255 字节用 1 字节前缀;>255 字节用 2 字节 —— 这个阈值看的是**字节长度**,不是字符数
索引、排序与临时表行为差异很实在
CHAR 字段建索引后,B+ 树节点能塞更多 key,范围扫描更快;VARCHAR 因长度可变,节点填充率略低,同等数据量下索引体积稍大、深度可能多一层。
更隐蔽的问题在排序:含 VARCHAR 的 GROUP BY 或 ORDER BY 若涉及大量数据,MySQL 可能被迫用磁盘临时表;而 CHAR 更大概率走内存排序。尤其当字段定义过宽(如 VARCHAR(1000))但实际只填几十字符时,优化器仍按最大可能长度预估内存需求,容易误判。
-
VARCHAR列无法设默认值(MySQL 5.7 及以前),CHAR可以 -
TEXT类型虽不属于本题,但常被拿来对比:它不支持完整列索引,VARCHAR(255)却可以建全文索引或函数索引 - MyISAM 表若启用
ROW_FORMAT=FIXED,VARCHAR也会变成定长存储,失去空间优势
真正难判断的不是“该用哪个”,而是“定义多长才合理”。比如手机号用 CHAR(11) 没问题,但地址字段写 VARCHAR(500) 很可能只是拍脑袋——先查下历史数据的 P95 长度,再加 20% 余量,比盲目拉长更有效。











