Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL CHAR与VARCHAR类型

MySQL中CHAR与VARCHAR类型举例解析

作者:NeoLshu

在MySQL数据库中CHAR和VARCHAR是两种常见的字符串数据类型,它们在存储和处理方式上有着显著的区别,这篇文章主要介绍了MySQL中CHAR与VARCHAR类型的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、存储机制与底层实现

1.1 CHAR 类型存储原理

CHAR 类型是 MySQL 中的定长字符串类型,其存储机制具有以下特点:

// MySQL 源码中的 CHAR 结构定义(简化)
struct CHAR_FIELD {
    uint32 length;        // 固定长度
    uchar *ptr;           // 指向存储空间的指针
    uchar array[FIXED_LENGTH]; // 实际存储空间
};

存储特性

-- 示例:CHAR(10) 存储不同长度数据
INSERT INTO char_table (char_col) VALUES 
    ('abc'),      -- 存储为 'abc       ' (7个空格)
    ('1234567890');-- 存储为 '1234567890' (无空格)

1.2 VARCHAR 类型存储原理

VARCHAR 是 MySQL 中的变长字符串类型,其存储结构如下:

// MySQL 源码中的 VARCHAR 结构定义(简化)
struct VARCHAR_FIELD {
    uint16 length_bytes;  // 长度前缀(1-2字节)
    uint32 max_length;    // 最大允许长度
    uchar *ptr;           // 指向实际数据的指针
};

存储特性

-- 示例:VARCHAR(10) 存储不同长度数据
INSERT INTO varchar_table (varchar_col) VALUES 
    ('abc'),      -- 存储为 '3abc' (1字节长度前缀+3字节数据)
    ('1234567890');-- 存储为 '101234567890' (1字节长度前缀+10字节数据)

1.3 行格式对存储的影响

MySQL 的行格式对 CHAR/VARCHAR 存储有显著影响:

行格式CHAR 处理VARCHAR 处理特点
COMPACT移除尾部空格保留尾部空格支持动态行
REDUNDANT保留尾部空格保留尾部空格传统格式
DYNAMIC移除尾部空格保留尾部空格大对象溢出页
COMPRESSED移除尾部空格保留尾部空格压缩存储

关键区别

二、性能对比与优化策略

2.1 读写性能分析

2.1.1 读取性能

2.1.2 写入性能

-- 性能测试示例
CREATE TABLE perf_test (
    id INT PRIMARY KEY,
    char_col CHAR(100),
    varchar_col VARCHAR(100)
) ENGINE=InnoDB;

-- 插入100万条随机长度数据
INSERT INTO perf_test
SELECT 
    n, 
    RPAD(UUID(), FLOOR(1 + RAND()*100), ' '),
    RPAD(UUID(), FLOOR(1 + RAND()*100), ' ')
FROM numbers;

测试结果

2.2 索引性能对比

2.2.1 索引结构影响

-- 创建索引对比
CREATE INDEX idx_char ON perf_test(char_col);
CREATE INDEX idx_varchar ON perf_test(varchar_col);

EXPLAIN SELECT * FROM perf_test WHERE char_col = 'test';
EXPLAIN SELECT * FROM perf_test WHERE varchar_col = 'test';

索引性能差异

2.2.2 索引大小对比

数据类型平均数据长度索引大小碎片率
CHAR(100)100字节220MB5%
VARCHAR(100)55字节130MB12%

2.3 内存使用优化

最佳实践

  1. 固定长度字段使用 CHAR(如 MD5、国家代码)
  2. 变长字段使用 VARCHAR(如用户名、地址)
  3. 避免过度分配 VARCHAR 长度
  4. 使用合适的字符集(latin1 vs utf8mb4)
  5. 定期优化表减少碎片
-- 优化示例
OPTIMIZE TABLE perf_test;
ALTER TABLE perf_test ROW_FORMAT=COMPRESSED;

三、字符集与排序规则的影响

3.1 字符集对存储的影响

字符集单字符字节数CHAR(10) 大小VARCHAR(10) 最大大小
latin11字节10字节10+1=11字节
utf83字节30字节30+1=31字节
utf8mb44字节40字节40+1=41字节

计算公式

3.2 排序规则的影响

-- 创建不同排序规则的表
CREATE TABLE collation_test (
    char_bin CHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin,
    char_ci CHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
    varchar_bin VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin,
    varchar_ci VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
);

-- 性能影响测试
SELECT * FROM collation_test ORDER BY char_bin;
SELECT * FROM collation_test ORDER BY varchar_ci;

性能差异

四、空间使用与碎片管理

4.1 存储空间计算

CHAR 空间计算公式

CHAR 存储空间 = 
   列定义长度 × 字符集最大字节长度

VARCHAR 空间计算公式

VARCHAR 存储空间 = 
   长度前缀(1或2字节) + 
   实际字符数 × 字符实际字节数

4.2 碎片管理策略

CHAR 碎片特点

VARCHAR 碎片特点

优化建议

-- 定期优化表
ALTER TABLE table_name ENGINE=InnoDB;

-- 使用合适行格式
ALTER TABLE table_name ROW_FORMAT=DYNAMIC;

-- 监控碎片情况
SELECT 
    TABLE_NAME,
    DATA_LENGTH,
    INDEX_LENGTH,
    DATA_FREE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database';

五、应用场景与最佳实践

5.1 CHAR 最佳使用场景

  1. 固定长度标识符

    -- 国家代码
    country_code CHAR(2) NOT NULL
    
  2. 加密哈希值

    -- MD5哈希
    password_hash CHAR(32) NOT NULL
    
  3. 标准化代码

    -- 产品代码
    product_sku CHAR(10) NOT NULL
    
  4. 性别标识

    -- 单字符存储
    gender CHAR(1) NOT NULL
    

5.2 VARCHAR 最佳使用场景

  1. 用户生成内容

    -- 用户名
    username VARCHAR(50) NOT NULL
    
  2. 地址信息

    -- 街道地址
    address_line VARCHAR(255) NOT NULL
    
  3. 描述性文本

    -- 产品描述
    description VARCHAR(1000) NULL
    
  4. 长文本片段

    -- 评论内容
    comment_text VARCHAR(2000) NULL
    

5.3 高级优化策略

  1. 长度阈值优化

    -- 255长度优化
    VARCHAR(255) -- 使用1字节长度前缀
    VARCHAR(256) -- 使用2字节长度前缀
    
  2. 混合类型设计

    -- 固定部分+可变部分
    product_id CHAR(6), -- 固定部分
    product_variant VARCHAR(20) -- 可变部分
    
  3. 字符集选择

    -- 根据内容选择字符集
    latin1_col VARCHAR(100) CHARACTER SET latin1,
    utf8mb4_col VARCHAR(100) CHARACTER SET utf8mb4
    
  4. 索引前缀优化

    -- 对长VARCHAR使用前缀索引
    CREATE INDEX idx_name ON users (last_name(10));
    

六、陷阱与常见问题

6.1 空格处理陷阱

-- 创建测试表
CREATE TABLE space_test (
    char_col CHAR(5),
    varchar_col VARCHAR(5)
);

-- 插入带空格数据
INSERT INTO space_test VALUES ('a', 'a'), ('a ', 'a ');

-- 查询比较
SELECT 
    CONCAT('[', char_col, ']') AS char_wrapped,
    CONCAT('[', varchar_col, ']') AS varchar_wrapped
FROM space_test;

查询结果

+--------------+-----------------+
| char_wrapped | varchar_wrapped |
+--------------+-----------------+
| [a]          | [a]             | -- 无空格
| [a]          | [a ]            | -- 有空格
+--------------+-----------------+

关键区别

6.2 隐式类型转换问题

-- 创建混合类型表
CREATE TABLE type_mix (
    id INT PRIMARY KEY,
    char_col CHAR(10),
    varchar_col VARCHAR(10)
);

-- 插入数据
INSERT INTO type_mix VALUES (1, 'test', 'test');

-- 查询比较
SELECT * FROM type_mix WHERE char_col = 'test';    -- 使用索引
SELECT * FROM type_mix WHERE varchar_col = 'test'; -- 使用索引

-- 使用整数比较
SELECT * FROM type_mix WHERE char_col = 0;    -- 全表扫描
SELECT * FROM type_mix WHERE varchar_col = 0; -- 全表扫描

性能影响

6.3 最大长度限制

CHAR 限制

VARCHAR 限制

行大小限制示例

-- 创建表测试行大小限制
CREATE TABLE row_size_test (
    col1 VARCHAR(10000),
    col2 VARCHAR(10000),
    col3 VARCHAR(10000)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- 错误:Row size too large (> 8126)

七、版本演进与最佳实践

7.1 MySQL 版本演进

版本CHAR 改进VARCHAR 改进
5.0 前尾部空格移除255字符限制
5.0.3不变支持65,535字节
5.5+更好的空格处理行格式优化
8.0+函数索引支持直方图统计

7.2 现代最佳实践

  1. 默认选择 VARCHAR

    • 更节省空间
    • 更符合现代应用需求
    • 性能差异在SSD上不明显
  2. 精确长度定义

    -- 精确指定长度
    country_code CHAR(2) -- 而非CHAR(10)
    username VARCHAR(50) -- 而非VARCHAR(255)
    
  3. 字符集优化

    -- 使用合适的字符集
    ALTER DATABASE db CHARACTER SET utf8mb4;
    
  4. 监控与调优

    -- 查看实际空间使用
    SELECT 
        TABLE_NAME,
        COLUMN_NAME,
        DATA_TYPE,
        CHARACTER_MAXIMUM_LENGTH,
        AVG_ROW_LENGTH
    FROM information_schema.COLUMNS
    WHERE TABLE_SCHEMA = 'your_db';
    

八、总结与决策指南

8.1 核心差异总结

特性CHARVARCHAR
存储方式定长变长
空格处理移除尾部空格保留尾部空格
存储空间固定分配动态分配
读取性能更高稍低
写入性能稍低更高
索引效率更高稍低
碎片率较高
最大长度255字符65,535字节

8.2 选择决策树

8.3 最终建议

  1. 优先选择 VARCHAR

    • 适用于大多数变长数据场景
    • 节省存储空间
    • 现代硬件上性能足够好
  2. 使用 CHAR 的场景

    • 严格固定长度的标识符(国家代码、哈希值)
    • 所有值长度几乎相同的列
    • 对尾部空格不敏感的字段
  3. 通用最佳实践

    • 始终明确定义长度
    • 使用合适的字符集
    • 定期优化表减少碎片
    • 避免过大的长度定义
    • 在关键索引列考虑性能差异

通过深入理解 CHAR 和 VARCHAR 的内部机制及性能特征,您可以根据具体应用场景做出最优选择,在存储效率、性能表现和开发便利性之间取得最佳平衡。

总结

到此这篇关于MySQL中CHAR与VARCHAR类型的文章就介绍到这了,更多相关MySQL CHAR与VARCHAR类型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文