CHAR 与 VARCHAR 的选择建议
基本区别
-- CHAR:定长字符串
CREATE TABLE t1 (col CHAR(10));
-- VARCHAR:变长字符串
CREATE TABLE t2 (col VARCHAR(10));
存储方式
| 特性 | CHAR | VARCHAR |
|---|---|---|
| 长度 | 固定 | 可变 |
| 存储空间 | 总是分配最大长度 | 实际长度 + 1-2 字节长度前缀 |
| 存储单位 | 字符 | 字符 + 额外字节 |
| 尾部空格 | 自动填充/截断 | 保留(MySQL 8.0 之前保留,之后取决于 sql_mode) |
| 最大长度 | 255 字符 | 65535 字节 |
实际存储对比
-- 存储 'ab' 在 CHAR(10) 中
-- 实际存储:'ab '(占 10 个字符)
-- 读取时自动去掉尾部空格
-- 存储 'ab' 在 VARCHAR(10) 中
-- 实际存储:2(长度前缀)+ 'ab'(占 3 个字节)
-- 读取时原样返回
性能对比
存储空间
CHAR(10) 存 'ab':占用 10 个字符的空间,剩余 8 个空格浪费了
VARCHAR(10) 存 'ab':占用 2 个字符 + 1 个字节长度前缀
VARCHAR 在大部分情况下节省空间
但 VARCHAR 的长度前缀本身也占用空间(1 字节或 2 字节)
查询性能
CHAR 的优势:
-- 1. 行长度固定,计算偏移更方便
-- InnoDB 处理定长行时,可以通过偏移量直接定位
-- 2. 页内行数更可预测
-- 如果所有列都是定长类型,一页能放的行数是固定的
-- 3. 更新不改变行大小
-- CHAR 列更新时不会导致页分裂(因为长度不变)
VARCHAR 的优势:
-- 1. 大多数情况下节省空间
-- 更小的数据占用 → 更多行在 buffer pool
-- → 更高的缓存命中率
-- 2. 表总大小更小
-- 备份更快、索引更小
如何选择
优先选择 VARCHAR 的场景
-- 1. 字段长度差异很大(如地址、描述)
VARCHAR(500) -- 大多数地址 20-100 字,少数才 500
-- 2. 字段最大长度远大于平均长度
VARCHAR(100) -- 名字平均 3-5 个字符
-- 3. 字段经常被更新
-- InnoDB 的 VARCHAR 更新可能会造成行迁移
-- (行大小变化导致原页面放不下)
-- 4. 经常创建索引
-- VARCHAR 索引更小,占用的磁盘和内存更少
-- 5. 多语言支持(UTF-8 字符)
VARCHAR(255) -- 在 UTF-8 下最多 765 字节
优先选择 CHAR 的场景
-- 1. 固定长度的标识符
CHAR(32) -- UUID、MD5
CHAR(11) -- 手机号(中国大陆 11 位)
CHAR(18) -- 身份证号(统一用 18 位)
-- 2. 非常短的字符串(几乎不浪费空间)
CHAR(1) -- 性别、状态:M/F, Y/N
CHAR(2) -- 省份代码:BJ, SH, GZ
-- 3. 长度高度统一的字段
CHAR(6) -- 邮政编码
CHAR(4) -- 验证码
-- 4. 需要存储固定格式便于比较
CHAR(32) -- MD5值固定32位,存CHAR(32)查询效率更高
核心经验法则
如果字段长度变化大 → VARCHAR
如果字段长度固定且很短(< 20) → CHAR
如果字段长度固定且很长(> 100) → VARCHAR(CHAR会浪费太多空间)
实际性能测试对比
假设一个用户表:
-- 方案 A:用 CHAR
CREATE TABLE user_char (
id INT,
name CHAR(20), -- 平均 6 字 × 3字节 = 18 字节
phone CHAR(11), -- 11 字节
email CHAR(100), -- 平均 20 × 3 = 60 字节
address CHAR(200) -- 平均 30 × 3 = 90 字节
);
-- 单行固定:4 + 20 + 11 + 100 + 200 = 335 字节
-- 方案 B:用 VARCHAR
CREATE TABLE user_varchar (
id INT,
name VARCHAR(20), -- 平均 18 + 1 = 19
phone VARCHAR(11), -- 11 + 1 = 12
email VARCHAR(100), -- 60 + 1 = 61
address VARCHAR(200) -- 90 + 1 = 91
);
-- 单行平均:4 + 19 + 12 + 61 + 91 = 187 字节
-- 节省约 44% 空间
在 buffer pool 大小为 1GB 时:
– CHAR:可缓存 1G / 335 = 约 320 万行
– VARCHAR:可缓存 1G / 187 = 约 570 万行
VARCHAR 在此时有 78% 的缓存优势。
常见误区
误区 1:VARCHAR(10) 和 VARCHAR(255) 存储相同的值占用一样空间
不对。虽然 ‘ab’ 在 VARCHAR(10) 和 VARCHAR(255) 中都只占 3 字节(2 + 1),但:
– VARCHAR(255) 的长度前缀为 1 字节
– VARCHAR(65535) 的长度前缀为 2 字节
– 更大的定义长度可能会影响内存中的临时表分配
– InnoDB 的行格式中,可变列的偏移列表定义可能更复杂
建议:VARCHAR 的长度应根据实际需求设定,不要随意给 255。
误区 2:CHAR 一定会更快
在现代 MySQL + InnoDB 下,CHAR 的”定长优势”已经不明显了。由于:
1. InnoDB 的页面管理机制
2. 变长行格式(COMPRESSED/DYNAMIC)
3. buffer pool 缓存机制
大多数场景下 VARCHAR 整体性能更好。
误区 3:CHAR 一定比 VARCHAR 更省空间
完全错误。对于同样存 ‘ab’:
– CHAR(10):10 字符(填充空格后)
– VARCHAR(10):2 字符 + 1 字节前缀
面试要点
- CHAR 和 VARCHAR 的根本区别是”定长” vs “变长”
- 选择的核心依据:字段长度是否统一 + 字段长度大小
- VARCHAR 是 90% 场景的默认选择
- CHAR 只在长度固定且短的字段(如状态码、定长 ID)中有优势
- VARCHAR 定义长度不要给太大,根据实际需求设置
- 理解存储引擎如何处理两者的底层存储差异
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容