CHAR 和 VARCHAR 选择建议

CHAR 和 VARCHAR 选择建议

基础区别

特性 CHAR VARCHAR
存储方式 定长 变长
最大长度 255 字符 65535 字节(受行大小限制)
存储空间 固定分配,不足补空格 实际长度 + 1~2 字节长度前缀
检索性能 快(固定偏移) 稍慢(需要读取长度前缀)
尾部空格 插入时截断 保留
比较时空格 忽略尾部空格 忽略尾部空格(SQL 标准)

存储与空间的详细对比

CHAR 存储

CREATE TABLE test (name CHAR(10));
INSERT INTO test VALUES ('abc');
-- 实际占用:10 个字节('abc' + 7 个空格补全)
-- 但比较时会忽略尾部空格,所以 'abc' = 'abc   '

注意:MySQL 从磁盘读取 CHAR(10) 时,只需要从固定偏移量读取固定长度的数据,计算简单。

VARCHAR 存储

CREATE TABLE test (name VARCHAR(10));
INSERT INTO test VALUES ('abc');
-- 实际占用:4 个字节(1 字节长度前缀 + 'abc' 3 个字符的字节数)
  • 长度前缀占 1 字节(长度 ≤ 255 时)或 2 字节(长度 > 255 时)
  • 实际存储的内容不包含尾部空格补全

如何选择

选 CHAR 的场景

1. 固定长度的标识符

-- 国家代码:固定 2 个字母
country_code CHAR(2)

-- MD5 哈希值:固定 32 个十六进制字符
password_hash CHAR(32)

-- UUID:固定 36 个字符
uuid CHAR(36)

-- 手机号(中国):固定 11 位数字
phone CHAR(11)

2. 存储长度变化很小的短字段

-- 性别:M/F 或 UNKNOWN
gender CHAR(1)

-- Y/N 标记
is_active CHAR(1)

-- 状态码(类似枚举)
status CHAR(4) -- PAID,PEND,DONE...

3. 需要频繁更新的字段

CHAR 是定长存储,更新时不会导致行迁移(row migration),而 VARCHAR 更新为更长的值时可能需要页分裂。

选 VARCHAR 的场景

1. 可变长度的描述性字段

-- 用户名:长度差异大
username VARCHAR(50)

-- 邮箱:长度差异大
email VARCHAR(255)

-- 地址:长度不确定
address VARCHAR(200)

2. 大部分数据长度远小于最大长度

-- 评论内容:最长 1000 字,但大部分只有几十个字
comment TEXT  VARCHAR(1000)

如果用 CHAR(1000),大部分行会浪费 900+ 字节的存储空间。

3. 字符串长度超过 255

CHAR 最大只支持 255 个字符。更长的字段只能用 VARCHARTEXT

对照表

字段示例 推荐类型 原因
国家代码 (2) CHAR(2) 固定长度
手机号 (11) CHAR(11) 固定长度
MD5 值 (32) CHAR(32) 固定长度
ISBN 号 (13) CHAR(13) 固定长度
IP 地址 VARCHAR(15) 变长(IPv4)
邮箱 VARCHAR(255) 变长
用户名 VARCHAR(50) 变长
文章标题 VARCHAR(200) 变长
身份证号 (18) CHAR(18) 固定长度
银行卡号 VARCHAR(30) 变长(不同银行不同)

InnoDB 的特殊行为

行溢出

InnoDB 的数据页大小默认 16KB,一行数据不能超过半个页(约 8KB)。当 VARCHAR 长度较大时:
– 短字符串:直接存储在行的数据页中(行内存储)
– 长字符串(通常超过 768 字节):存储在溢出页(off-page),数据页中只保留 768 字节前缀和指针

-- 定义 VARCHAR(1000),InnoDB 不会在每行都预留 1000 字节
-- 而是在必要时使用溢出页
description VARCHAR(1000)

这意味着 VARCHAR(1000) 并不一定比 VARCHAR(100) 占用更多行内空间。

性能考虑

行大小与页密度

-- 如果大部分行的 name 是 5~10 字符
-- CHAR(100) 每行浪费 90+ 字节
-- 一页(16KB)能存放的行数减少 → 更多 I/O

-- VARCHAR(100) 按实际长度存储
-- 一页能存放更多的行 → 更高效的缓存和查询

排序性能

CHAR 的排序比 VARCHAR 稍快,因为长度固定,计算偏移量简单。但在实际应用中,差异非常小,通常可以忽略。

MySQL 8.0 的变化

MySQL 8.0 对字符集的处理有了变化:
– 默认字符集从 latin1 改为 utf8mb4
utf8mb4 中一个字符最多占 4 字节
– VARCHAR(255) 在 utf8mb4 下最大占用 1020 字节 + 2 字节长度前缀

这意味在 utf8mb4 下,VARCHAR(255) 已经接近 InnoDB 的 768 字节行内限制,需要注意溢出页问题。

总结

在选择 CHAR 和 VARCHAR 时的核心原则:

  1. 先看长度是否固定:固定长度用 CHAR,变化大用 VARCHAR
  2. 再看最大长度:超过 255 只能用 VARCHAR 或 TEXT
  3. 结合业务场景:短标识符用 CHAR,描述性文本用 VARCHAR
  4. 考虑存储效率:大部分短而少数长时,CHAR 会导致空间浪费
  5. 考虑更新频率:高频更新 CHAR 优于 VARCHAR(避免行迁移)

总之:不确定时默认用 VARCHAR。只有在明确字段长度固定、且长度较短(< 50)且频繁更新时,CHAR 才是更好的选择。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容