这是一个在 MySQL 面试和日常开发中非常经典的问题。CHAR 和 VARCHAR 是两种最常用的字符串数据类型,它们的核心区别在于存储和检索的方式。
下面我将从多个维度详细解释它们的区别,并给出选择建议。
核心区别总结
| 特性 | CHAR | VARCHAR |
|---|---|---|
| 长度处理 | 固定长度 | 可变长度 |
| 存储方式 | 分配定义的全部空间,不足部分用空格填充 | 只存储实际字符数(+1或+2字节存储长度信息) |
| 存储空间 | 固定,为声明的长度 | 可变,为实际字符串长度 + 长度前缀 |
| 尾部空格 | 检索时会自动删除 | 检索时会保留 |
| 读取速度 | 较快(固定长度,无需计算) | 稍慢(需要根据长度信息读取) |
| 适用场景 | 长度固定或近乎固定的数据(如MD5、UUID、状态码) | 长度变化较大的数据(如姓名、地址、描述) |
详细解释
1. 存储方式与空间占用
-
CHAR(n):-
你声明一个
CHAR(10)的列,无论你存入"hi"(2字符)还是"hello"(5字符),MySQL 都会在磁盘上分配并占用 10 个字符的空间。 -
如果存入的字符串长度不足,MySQL 会在其右侧用空格填充到指定长度。
-
例如,存储
"A"到CHAR(4)列,实际存储的是"A "。
-
-
VARCHAR(n):-
你声明一个
VARCHAR(10)的列,存入"hi"大约占用 3 个字节(2个字符 + 1字节的长度前缀),存入"hello"大约占用 6 个字节(5个字符 + 1字节的长度前缀)。 -
VARCHAR需要使用额外的 1 或 2 个字节来存储"字符串的长度"这个信息。-
如果声明的最大长度
n<= 255,则使用 1 个字节 作为长度前缀。 -
如果声明的最大长度
n> 255,则使用 2 个字节 作为长度前缀。
-
-
它只存储实际的字符串内容,不会用空格填充。
-
2. 尾部空格的处理(关键行为差异)
这是另一个非常重要的区别,尤其是在进行字符串比较时。
-
CHAR:在检索(SELECT)时会自动移除存储时填充的尾部空格。sql-- 假设有一个 CHAR(5) 的列 INSERT INTO table (char_column) VALUES ('abc '); -- 存储为 'abc ' SELECT char_column, LENGTH(char_column) FROM table; -- 检索结果:'abc',长度为 3(尾部空格被移除) -
VARCHAR:在检索时会保留尾部空格。sql-- 假设有一个 VARCHAR(5) 的列 INSERT INTO table (varchar_column) VALUES ('abc '); -- 存储 'abc ' 及其长度信息 SELECT varchar_column, LENGTH(varchar_column) FROM table; -- 检索结果:'abc ',长度为 5(尾部空格被保留)
注意 :由于
CHAR的这种行为,当使用=比较时:
'abc '(CHAR)和'abc'(CHAR)是相等的;
'abc '(CHAR)和'abc'(VARCHAR)是相等的;
'abc '(VARCHAR)和'abc'(VARCHAR)是不相等的。
3. 性能考量
-
CHAR:-
优点 :由于长度固定,在磁盘上的记录也是定长的,所以读写速度通常更快。MySQL 可以很容易地计算出第 N 条记录的位置,尤其是在 MyISAM 这种存储引擎中,对于全表扫描或频繁更新的场景有优势。
-
缺点:可能会浪费存储空间,尤其是当存储的数据长度远小于定义长度时。
-
-
VARCHAR:-
优点 :节省存储空间。
-
缺点 :由于记录长度可变,更新数据可能会导致行的大小发生变化,从而可能引发页分裂(对于 InnoDB),影响一点性能。此外,读取时需要先读取长度信息,再读取具体数据,过程稍复杂。
-
如何选择?
请根据你的业务场景来决定:
-
使用
CHAR的情况:-
存储的字符串长度非常固定或变化极小。
-
经典例子:
-
MD5/SHA1 哈希值(长度固定,如 MD5 总是 32字符)
-
UUID (虽然可以用
CHAR(36),但现在更推荐存储为二进制或使用UUID_SHORT()) -
国家代码 (如
CHAR(2)用于 'US', 'CN') -
定长的状态码或标志位 (如
CHAR(1)用于 'Y'/'N')
-
-
-
避免使用CHAR的情况:
- 需要精确保留字符串的原始格式(包括尾部空格)
-
使用
VARCHAR的情况:- 存储的字符串长度变化很大。
一个简单的例子
sql
CREATE TABLE test_string (
id INT PRIMARY KEY,
fixed_char CHAR(5),
variable_varchar VARCHAR(5)
);
INSERT INTO test_string (id, fixed_char, variable_varchar) VALUES
(1, 'A', 'A'), -- CHAR存储 'A ', VARCHAR存储 'A'
(2, 'ABCDE', 'ABCDE'); -- 两者都存满
-- 查询并显示长度
SELECT
fixed_char,
LENGTH(fixed_char) as char_length,
variable_varchar,
LENGTH(variable_varchar) as varchar_length
FROM test_string;
结果可能如下:
| fixed_char | char_length | variable_varchar | varchar_length |
|---|---|---|---|
| A | 1 | A | 1 |
| ABCDE | 5 | ABCDE | 5 |
总结
-
追求空间效率,数据长度多变 -> 选择
VARCHAR。这是绝大多数场景下的选择。 -
追求极致性能,数据长度固定 -> 选择
CHAR。 -
深刻理解
CHAR会剔除检索结果的尾部空格 这一点,可以避免很多意想不到的查询 Bug。
在现代 MySQL 版本和 InnoDB 存储引擎下,对于大多数通用场景,VARCHAR 因其灵活性而更受欢迎。除非你非常确定某个字段的长度是绝对固定的,否则从 VARCHAR 开始通常是一个安全的选择。