MySQL 字符集与排序规则
字符集和排序规则是数据库处理文本数据的基础。正确配置字符集不仅能避免中文乱码问题,还能确保数据的正确存储和查询。本章将详细介绍 MySQL 字符集与排序规则的概念、配置和最佳实践。
基本概念
什么是字符集?
字符集(Character Set)是一套符号和编码的集合。它定义了哪些字符可以被存储,以及每个字符如何被编码为二进制数据。
打个比方,字符集就像是一本"字典",告诉计算机如何将人类可读的字符(如"中"、"A"、"😊")转换为计算机可存储的二进制数据。
字符集示例:
┌─────────────────────────────────────────────┐
│ 字符 │ 编码(十六进制)│ 二进制 │
├─────────────────────────────────────────────┤
│ A │ 41 │ 01000001 │
│ 中 │ E4B8AD │ 111001001011100010101101 │
│ 😊 │ F09F988A │ 11110000100111111001100010001010 │
└─────────────────────────────────────────────┘
什么是排序规则?
排序规则(Collation)定义了字符集中字符的比较和排序规则。它决定了:
- 字符如何比较大小
- 字符串如何排序
- 查询时是否区分大小写
- 查询时是否区分重音
例如,在 utf8mb4_general_ci 排序规则中:
ci表示 case-insensitive(不区分大小写)A和a被视为相同- 排序时
Apple和apple可能相邻
字符集与排序规则的关系
一个字符集可以有多个排序规则,但一个排序规则只能属于一个字符集:
utf8mb4 字符集
├── utf8mb4_general_ci (通用排序,不区分大小写)
├── utf8mb4_0900_ai_ci (MySQL 8.0 默认,基于 UCA 9.0)
├── utf8mb4_bin (二进制比较,区分大小写)
├── utf8mb4_unicode_ci (基于 UCA 4.0)
└── utf8mb4_unicode_520_ci(基于 UCA 5.2)
MySQL 支持的字符集
查看支持的字符集
-- 查看所有支持的字符集
SHOW CHARACTER SET;
-- 查看特定字符集
SHOW CHARACTER SET LIKE 'utf8%';
-- 从 information_schema 查询
SELECT
CHARACTER_SET_NAME,
DEFAULT_COLLATE_NAME,
DESCRIPTION,
MAXLEN
FROM information_schema.CHARACTER_SETS
WHERE CHARACTER_SET_NAME LIKE 'utf8%';
常用字符集对比
| 字符集 | 说明 | 每字符最大字节 | 推荐场景 |
|---|---|---|---|
utf8mb4 | UTF-8 完整实现 | 4 字节 | 推荐使用,支持所有 Unicode 字符 |
utf8mb3 | UTF-8 子集(已弃用) | 3 字节 | 旧系统兼容,不支持 Emoji |
utf8 | utf8mb3 的别名 | 3 字节 | 不推荐,MySQL 8.0 已弃用 |
latin1 | 西欧字符集 | 1 字节 | 旧系统,不支持中文 |
gbk | 中文 GBK 编码 | 2 字节 | 特定中文场景 |
gb18030 | 中国国家标准 | 4 字节 | 中国政府项目要求 |
big5 | 繁体中文 | 2 字节 | 港台地区 |
utf8 vs utf8mb4 的关键区别
这是 MySQL 中一个著名的"陷阱":
-- utf8 实际上是 utf8mb3 的别名,只支持最多 3 字节的字符
-- 无法存储 Emoji 和部分生僻字
-- 尝试存储 Emoji
CREATE TABLE test_utf8 (
content VARCHAR(100) CHARACTER SET utf8
);
INSERT INTO test_utf8 VALUES ('😊');
-- 错误:Incorrect string value: '\xF0\x9F\x98\x8A'
-- utf8mb4 支持完整的 UTF-8,包括 Emoji
CREATE TABLE test_utf8mb4 (
content VARCHAR(100) CHARACTER SET utf8mb4
);
INSERT INTO test_utf8mb4 VALUES ('😊');
-- 成功!
重要结论:永远使用 utf8mb4,不要使用 utf8。
字符集的存储开销
字符集影响字符串的存储空间计算:
-- VARCHAR(n) 中的 n 是字符数,不是字节数
-- utf8mb4 每个字符最多占 4 字节
-- VARCHAR(100) 在 utf8mb4 下的最大存储空间
-- 100 字符 × 4 字节 + 2 字节长度前缀 = 402 字节
-- 创建表时注意索引长度限制
CREATE TABLE articles (
title VARCHAR(255), -- utf8mb4 下可能超过索引长度限制
INDEX idx_title (title(191)) -- 191 × 4 = 764 字节,小于 767 字节限制
);
排序规则详解
查看排序规则
-- 查看所有排序规则
SHOW COLLATION;
-- 查看特定字符集的排序规则
SHOW COLLATION WHERE Charset = 'utf8mb4';
-- 从 information_schema 查询
SELECT
COLLATION_NAME,
CHARACTER_SET_NAME,
IS_DEFAULT,
IS_COMPILED
FROM information_schema.COLLATIONS
WHERE CHARACTER_SET_NAME = 'utf8mb4'
ORDER BY COLLATION_NAME;
排序规则命名规则
MySQL 排序规则名称遵循一定的命名规范:
字符集_版本_特殊属性_后缀
示例:utf8mb4_0900_ai_ci
│ │ │ │
│ │ │ └── ci: case insensitive(不区分大小写)
│ │ └───── ai: accent insensitive(不区分重音)
│ └────────── 0900: UCA 9.0.0 版本
└────────────────── utf8mb4 字符集
常见后缀含义:
| 后缀 | 英文 | 含义 |
|---|---|---|
_ci | Case Insensitive | 不区分大小写 |
_cs | Case Sensitive | 区分大小写 |
_bin | Binary | 二进制比较,区分大小写 |
_ai | Accent Insensitive | 不区分重音 |
_as | Accent Sensitive | 区分重音 |
MySQL 8.0 默认排序规则
MySQL 8.0 的默认排序规则是 utf8mb4_0900_ai_ci:
- 0900:基于 Unicode Collation Algorithm (UCA) 9.0.0
- ai:不区分重音(accent insensitive)
- ci:不区分大小写(case insensitive)
-- 查看服务器默认排序规则
SHOW VARIABLES LIKE 'collation_server';
-- 查看数据库默认排序规则
SHOW VARIABLES LIKE 'collation_database';
排序规则的选择
场景一:需要区分大小写
-- 使用 _bin 或 _cs 排序规则
CREATE TABLE users (
username VARCHAR(50) COLLATE utf8mb4_bin,
email VARCHAR(100)
);
-- 查询时区分大小写
SELECT * FROM users WHERE username = 'Admin'; -- 只匹配 'Admin',不匹配 'admin'
场景二:多语言支持
-- 使用 Unicode 排序规则,支持多种语言的正确排序
CREATE TABLE products (
name VARCHAR(100) COLLATE utf8mb4_unicode_ci
);
-- 中文、日文、韩文等都能正确排序
SELECT * FROM products ORDER BY name;
场景三:性能优先
-- _general_ci 比 _unicode_ci 更快,但排序准确性稍低
CREATE TABLE logs (
message VARCHAR(1000) COLLATE utf8mb4_general_ci
);
排序规则对比:
| 排序规则 | 性能 | 准确性 | 适用场景 |
|---|---|---|---|
utf8mb4_general_ci | 高 | 一般 | 简单应用,性能优先 |
utf8mb4_unicode_ci | 中 | 好 | 多语言应用 |
utf8mb4_0900_ai_ci | 中 | 最好 | MySQL 8.0+ 默认,推荐使用 |
utf8mb4_bin | 高 | 精确 | 需要区分大小写 |
排序规则对查询的影响
-- 创建测试表
CREATE TABLE test_collation (
id INT PRIMARY KEY,
name VARCHAR(50) COLLATE utf8mb4_0900_ai_ci
);
INSERT INTO test_collation VALUES
(1, 'Apple'),
(2, 'apple'),
(3, 'APPLE'),
(4, '香蕉'),
(5, '苹果');
-- 不区分大小写查询
SELECT * FROM test_collation WHERE name = 'apple';
-- 返回 id=1, 2, 3 的所有记录
-- 区分大小写查询(使用 BINARY)
SELECT * FROM test_collation WHERE BINARY name = 'apple';
-- 只返回 id=2
-- 使用 COLLATE 子句临时指定排序规则
SELECT * FROM test_collation
WHERE name COLLATE utf8mb4_bin = 'apple';
-- 只返回 id=2
-- 排序结果
SELECT * FROM test_collation ORDER BY name;
-- 结果:Apple, apple, APPLE, 香蕉, 苹果(不区分大小写排序)
字符集设置层级
MySQL 支持在多个层级设置字符集和排序规则,层级从高到低为:
列级设置(优先级最高)
↓
表级设置
↓
数据库级设置
↓
服务器级设置(优先级最低)
服务器级别
服务器级别的字符集是所有数据库的默认值:
-- 查看服务器字符集设置
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'collation_server';
-- 启动时设置(my.cnf 或 my.ini)
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
-- 运行时修改(需要 SUPER 权限)
SET GLOBAL character_set_server = utf8mb4;
SET GLOBAL collation_server = utf8mb4_0900_ai_ci;
数据库级别
-- 创建数据库时指定
CREATE DATABASE mydb
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 修改数据库字符集
ALTER DATABASE mydb
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 查看数据库字符集
SHOW CREATE DATABASE mydb;
-- 查看当前数据库设置
SELECT
@@character_set_database,
@@collation_database;
注意:修改数据库字符集不会自动修改已有表的字符集,只影响新创建的表。
表级别
-- 创建表时指定
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50),
email VARCHAR(100)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- 修改表字符集
ALTER TABLE users
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 查看表字符集
SHOW CREATE TABLE users;
注意:修改表字符集不会自动修改已有列的字符集,只影响新添加的列。
列级别
列级别的设置优先级最高:
-- 创建表时指定列字符集
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
description TEXT CHARACTER SET utf8mb4,
code VARCHAR(20) CHARACTER SET latin1 -- 特殊需求
);
-- 修改列字符集
ALTER TABLE products
MODIFY name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
-- 转换列字符集(同时转换数据)
ALTER TABLE products
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
连接字符集
连接字符集影响客户端与服务器之间的通信:
-- 查看连接相关字符集
SHOW VARIABLES LIKE 'character_set%';
-- 输出示例:
-- character_set_client = utf8mb4 -- 客户端发送的语句字符集
-- character_set_connection = utf8mb4 -- 连接字符集
-- character_set_database = utf8mb4 -- 当前数据库字符集
-- character_set_results = utf8mb4 -- 返回结果的字符集
-- character_set_server = utf8mb4 -- 服务器默认字符集
-- character_set_system = utf8 -- 系统元数据字符集(固定)
-- 设置连接字符集
SET NAMES utf8mb4; -- 同时设置 client、connection、results
-- 或分别设置
SET character_set_client = utf8mb4;
SET character_set_connection = utf8mb4;
SET character_set_results = utf8mb4;
配置文件设置:
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
中文支持最佳实践
推荐配置
对于中文应用,推荐以下配置:
-- 创建数据库
CREATE DATABASE app_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 创建表
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT,
author VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
排序规则选择
对于中文数据,排序规则的选择主要看是否需要区分大小写:
-- 场景一:不需要区分大小写(推荐)
-- 使用 utf8mb4_0900_ai_ci(MySQL 8.0+ 默认)
CREATE TABLE users (
username VARCHAR(50) -- 'Admin' 和 'admin' 视为相同
);
-- 场景二:需要区分大小写
-- 使用 utf8mb4_bin 或查询时使用 BINARY
CREATE TABLE users (
username VARCHAR(50) COLLATE utf8mb4_bin
);
-- 或查询时区分
SELECT * FROM users WHERE BINARY username = 'Admin';
中文排序问题
MySQL 的 Unicode 排序规则对中文的排序是按 Unicode 编码顺序:
-- 创建测试数据
CREATE TABLE chinese_test (
name VARCHAR(50)
) CHARACTER SET utf8mb4;
INSERT INTO chinese_test VALUES
('张三'), ('李四'), ('王五'), ('赵六'), ('陈七');
-- 默认排序(按 Unicode 编码)
SELECT name FROM chinese_test ORDER BY name;
-- 结果:李四、王五、张三、赵六、陈七
-- 按拼音排序(需要特定排序规则或函数)
SELECT name FROM chinese_test
ORDER BY CONVERT(name USING gbk);
-- 结果:陈七、李四、王五、张三、赵六
按拼音排序的实现方式:
-- 方式一:使用 GBK 字符集排序
SELECT name FROM chinese_test
ORDER BY CONVERT(name USING gbk);
-- 方式二:存储拼音首字母
ALTER TABLE chinese_test ADD COLUMN pinyin VARCHAR(10);
UPDATE chinese_test SET pinyin = 'Z'; -- 张三
-- 使用拼音首字母排序
SELECT name FROM chinese_test ORDER BY pinyin;
字符集转换
转换已有数据
将已有表的字符集转换为 utf8mb4:
-- 1. 查看当前字符集
SHOW CREATE TABLE mytable;
-- 2. 转换表字符集(包括数据)
ALTER TABLE mytable
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 3. 转换数据库中所有表
-- 生成转换脚本
SELECT CONCAT(
'ALTER TABLE ', TABLE_NAME,
' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;'
) AS sql_statement
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'mydb'
AND TABLE_TYPE = 'BASE TABLE';
-- 4. 执行生成的语句
转换注意事项
-- 警告:转换可能导致数据丢失!
-- 1. 先备份数据
-- mysqldump -u root -p mydb > backup.sql
-- 2. 检查是否有不兼容的字符
SELECT * FROM mytable
WHERE HEX(content) REGEXP '^[^0-9A-Fa-f]';
-- 3. 转换特定列
ALTER TABLE mytable
MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
字符集转换函数
-- CONVERT 函数:转换字符集
SELECT CONVERT('中文' USING utf8mb4);
-- CAST 函数:指定字符集
SELECT CAST('中文' AS CHAR CHARACTER SET utf8mb4);
-- HEX 函数:查看十六进制编码
SELECT HEX('中'); -- E4B8AD
-- UNHEX 函数:从十六进制还原
SELECT UNHEX('E4B8AD'); -- 中
常见问题与解决方案
问题一:中文乱码
原因分析:字符集不匹配,通常是因为:
- 表或列使用了 latin1 字符集
- 连接字符集与数据字符集不一致
- 客户端显示字符集设置错误
解决方案:
-- 1. 检查各层级的字符集设置
SHOW VARIABLES LIKE 'character%';
SHOW CREATE TABLE your_table;
-- 2. 设置连接字符集
SET NAMES utf8mb4;
-- 3. 修复表的字符集
ALTER TABLE your_table
CONVERT TO CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
-- 4. 如果数据已经乱码,需要修复
-- 假设 latin1 存储了 utf8mb4 数据
ALTER TABLE your_table MODIFY content BLOB;
ALTER TABLE your_table
MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
问题二:Emoji 无法存储
原因:使用了 utf8(即 utf8mb3)字符集,无法存储 4 字节的 Emoji。
解决方案:
-- 1. 转换为 utf8mb4
ALTER TABLE your_table
MODIFY content VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- 2. 确保连接也使用 utf8mb4
SET NAMES utf8mb4;
问题三:索引长度超限
原因:utf8mb4 每字符最多 4 字节,VARCHAR(255) 可能超过索引长度限制。
解决方案:
-- 方法一:限制索引长度
CREATE TABLE users (
email VARCHAR(255),
INDEX idx_email (email(191)) -- 191 × 4 = 764 字节
);
-- 方法二:使用前缀索引
CREATE INDEX idx_email ON users(email(100));
-- 方法三:MySQL 5.7+ 启用 innodb_large_prefix
SET GLOBAL innodb_large_prefix = ON;
SET GLOBAL innodb_file_format = 'Barracuda';
-- MySQL 8.0 默认已启用大索引前缀
问题四:查询不区分大小写
原因:默认的 _ci 排序规则不区分大小写。
解决方案:
-- 方法一:使用 BINARY 关键字
SELECT * FROM users WHERE BINARY username = 'Admin';
-- 方法二:使用 _bin 排序规则
SELECT * FROM users
WHERE username COLLATE utf8mb4_bin = 'Admin';
-- 方法三:修改列的排序规则
ALTER TABLE users
MODIFY username VARCHAR(50) COLLATE utf8mb4_bin;
问题五:导入数据乱码
解决方案:
# 导出时指定字符集
mysqldump -u root -p --default-character-set=utf8mb4 mydb > backup.sql
# 导入时指定字符集
mysql -u root -p --default-character-set=utf8mb4 mydb < backup.sql
最佳实践总结
配置清单
# my.cnf / my.ini 配置
[mysqld]
# 服务器字符集
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
# 初始化连接字符集
init_connect='SET NAMES utf8mb4'
[client]
# 客户端默认字符集
default-character-set=utf8mb4
[mysql]
# 命令行客户端字符集
default-character-set=utf8mb4
开发规范
- 统一使用 utf8mb4:新项目全部使用 utf8mb4,避免 utf8 别名
- 明确指定字符集:创建数据库和表时显式指定字符集
- 连接字符集一致:确保应用连接时设置正确的字符集
- 测试中文和 Emoji:开发阶段测试各种字符的存储和查询
- 注意索引长度:utf8mb4 下 VARCHAR 索引注意长度限制
迁移检查清单
- 备份数据
- 检查当前字符集配置
- 确认没有使用 utf8 别名
- 测试转换脚本
- 执行字符集转换
- 验证数据完整性
- 测试应用功能
- 更新配置文件
- 监控运行状态
小结
本章我们学习了:
- 基本概念:字符集定义字符的编码方式,排序规则定义比较和排序规则
- 字符集选择:永远使用 utf8mb4,不要使用 utf8(utf8mb3)
- 排序规则:MySQL 8.0 默认 utf8mb4_0900_ai_ci,支持多语言
- 设置层级:服务器、数据库、表、列四级设置,列级优先级最高
- 中文支持:使用 utf8mb4,注意排序规则选择和拼音排序实现
- 常见问题:乱码、Emoji、索引长度、大小写敏感的处理方法
练习
- 创建一个支持中文和 Emoji 的数据库和表
- 测试不同排序规则下的大小写敏感性
- 实现一个按拼音首字母排序的查询
- 将一个 latin1 字符集的表转换为 utf8mb4