mysql中 char 和 varchar 的区别

这是一个在 MySQL 面试和日常开发中非常经典的问题。CHARVARCHAR 是两种最常用的字符串数据类型,它们的核心区别在于存储和检索的方式

下面我将从多个维度详细解释它们的区别,并给出选择建议。

核心区别总结

特性 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 开始通常是一个安全的选择。

相关推荐
野生技术架构师1 小时前
数据库连接池爆满如何排查
网络·数据库·oracle
百***81271 小时前
从 SQL 语句到数据库操作
数据库·sql·oracle
SelectDB2 小时前
深入理解 Doris Variant:如何让 JSON 查询性能追平列存,还能承载万列索引字段?|Deep Dive
大数据·数据库·数据分析
百***3282 小时前
数据库(MySQL):使用命令从零开始在Navicat创建一个数据库及其数据表(一).创建基础表
数据库·mysql·oracle
q***d1732 小时前
SQL性能调优
数据库·sql
q***T5832 小时前
MySQL爬虫
数据库·爬虫·mysql
q***72564 小时前
【JOIN】关键字在MySql中的详细使用
数据库·mysql
萤火夜4 小时前
MYSQL之事务
数据库·mysql
q***R3084 小时前
MySQL并发
数据库·mysql