字符集与排序规则
本教程共 46 篇 · 第 11 篇 · 更新于 2026-07-30 · 约 13 分钟阅读
11. 字符集与排序规则
本节目标:彻底搞懂 MySQL 的字符集(Character Set)和排序规则(Collation),明白 utf8mb4 和 utf8 到底差在哪、collation 命名怎么看、四个层级怎么设置,学会正确存中文和 emoji,告别乱码。
乱码是中文开发者绕不开的痛。明明能存中文,怎么存 emoji 就报错?为什么 WHERE name = 'Tom' 能查到 'tom'?这些都和字符集、排序规则有关。这一节把这些一次性讲透。
11.1 什么是字符集
字符集(Character Set) 是一套”字符和数字编码”的对应表。它规定了某个字符在计算机里用哪个数字表示。
比如 utf8mb4 字符集下:
A对应数字 65;中对应数字 20013;- emoji
😀对应数字 128512。
字符集决定了”能存哪些字符”以及”每个字符占几个字节”。
查看 MySQL 支持的所有字符集:
SHOW CHARACTER SET;
输出(截选):
+----------+---------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------+---------------------+--------+
| ascii | US ASCII | ascii_general_ci | 1 |
| latin1 | cp1252 West European | latin1_swedish_ci | 1 |
| utf8mb3 | UTF-8 Unicode | utf8mb3_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
| binary | Binary pseudo charset | binary | 1 |
+----------+---------------------------+---------------------+--------+
Maxlen 列很重要:一个字符最多占几个字节。
11.2 utf8mb4 vs utf8:最关键的坑
这是新手必踩的坑,一定要讲清楚。
MySQL 里有两个长得像的字符集:
utf8(即utf8mb3):最多 3 字节,存不下 emoji 和部分生僻字;utf8mb4:最多 4 字节,完整支持 Unicode,能存 emoji。
WarningMySQL 里的
utf8是”残废版”,最多 3 字节!而标准的 UTF-8 是 1~4 字节。所以在 MySQL 里说utf8≠ 标准的 UTF-8。这就是为什么用utf8存 emoji 会报Incorrect string value错误。
版本演进:
- 8.0 之前:默认字符集是
latin1,utf8指 3 字节版; - 8.0 起:默认字符集改成
utf8mb4,默认排序规则utf8mb4_0900_ai_ci; - 26.7:默认就是
utf8mb4。
Tip从 8.0 起,
utf8被当作utf8mb3的别名,官方计划未来把utf8改成指utf8mb4。但只要你看到老教程写utf8,一律在心里替换成utf8mb4。新项目无脑用utf8mb4。
emoji 存储实验
-- 建表用 utf8mb4
CREATE TABLE users (
id INT PRIMARY KEY,
nickname VARCHAR(50)
) CHARACTER SET utf8mb4;
-- 存 emoji,没问题
INSERT INTO users VALUES(1, '码上学😀');
mysql> SELECT nickname FROM users;
+-----------+
| nickname |
+-----------+
| 码上学😀 |
+-----------+
要是表用 utf8(utf8mb3),同样这条插入就报错。所以只要可能存 emoji、生僻字、多语言,就用 utf8mb4。
11.3 什么是排序规则
排序规则(Collation) 是字符集的一套比较和排序规则。光有字符集知道”字符是啥”还不够,还得知道”字符之间怎么比大小、怎么排序”。
比如同样是 utf8mb4:
utf8mb4_0900_ai_ci:不区分大小写、不区分重音(A = a = á);utf8mb4_0900_as_cs:区分大小写、区分重音(A ≠ a ≠ á);utf8mb4_bin:按二进制字节比较,最严格。
排序规则决定了两件事:
- 排序:
ORDER BY时谁排前谁排后; - 比较:
WHERE name = 'tom'能不能匹配到'Tom'。
查看某个字符集有哪些排序规则:
SHOW COLLATION LIKE 'utf8mb4%';
11.4 排序规则的命名规则
排序规则的名字有固定格式,看懂后缀就明白它的行为:
字符集名_版本_敏感性后缀
常见后缀:
| 后缀 | 含义 | 示例 |
|---|---|---|
_ci | Case Insensitive,不区分大小写 | utf8mb4_0900_ai_ci |
_cs | Case Sensitive,区分大小写 | utf8mb4_0900_as_cs |
_bin | 二进制比较,区分一切 | utf8mb4_bin |
_ai | Accent Insensitive,不区分重音 | (和 _ci 搭配) |
_as | Accent Sensitive,区分重音 | (和 _cs 搭配) |
utf8mb4_0900_ai_ci 拆开看:
utf8mb4:字符集;0900:基于 UCA(Unicode Collation Algorithm)9.0.0 排序算法;ai:不区分重音(a = á);ci:不区分大小写(A = a)。
这是 8.0+ 的默认排序规则,也是 26.7 的默认值。
Note默认的
_ci排序规则意味着WHERE name = 'Tom'能查到'tom'、'TOM'。如果你希望大小写敏感(比如用户名不能重复),要么改成_cs/_bin,要么在查询时用BINARY强制比较:WHERE BINARY name = 'Tom'。
11.5 字符集的四个层级
MySQL 的字符集可以在四个层级设置,优先级从高到低:列 > 表 > 数据库 > 服务器。低层级没指定就继承上一级。
服务器级
服务器启动时设定的全局字符集,影响所有新库。在 my.cnf 配置:
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
[client]
default-character-set=utf8mb4
查看当前服务器字符集:
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'collation_server';
数据库级
建库时指定,没指定就继承服务器级:
CREATE DATABASE shop
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
改库的字符集:
ALTER DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
Warning改库字符集只影响之后新建的表,已存在的表不会自动跟着变。要改旧表,得逐个
ALTER TABLE转换。
表级
建表时指定,没指定就继承库级:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CHARSET 是 CHARACTER SET 的简写,效果一样。
列级
只给某个字符串列单独指定:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin
);
这样 name 列就是二进制比较,区分大小写,而其他列继承表的设置。
Tip列级设置优先级最高。如果某列需要特殊比较规则(如密码列要区分大小写),单独给这列设
utf8mb4_bin即可,不用动整张表。
11.6 连接字符集:客户端和服务端要对上
还有一个容易出乱码的地方:客户端和服务端之间的字符集。如果客户端用 UTF-8 发数据,服务端当成 latin1 接收,就会乱码。
相关变量:
character_set_client:客户端发来的数据是什么字符集;character_set_connection:连接层用的字符集;character_set_results:返回结果给客户端时用的字符集。
最省事的做法是统一用一条命令把这三者设成 utf8mb4:
SET NAMES utf8mb4;
或者在连接时指定:
mysql --default-character-set=utf8mb4 -u root -p
Note
SET NAMES utf8mb4不是改服务器或库的字符集,它只影响”这次连接怎么收发数据”。每次连接都要设,或写进配置文件一劳永逸。
11.7 查看字符集的常用命令
汇总几个排查乱码时必用的命令:
-- 服务器字符集和排序规则
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'collation_server';
-- 当前连接相关字符集
SHOW VARIABLES LIKE 'character_set%';
-- 某个库的字符集
SHOW CREATE DATABASE shop;
-- 某张表的字符集
SHOW CREATE TABLE users;
-- 某列的字符集(看 Collation 列)
SHOW FULL COLUMNS FROM users;
Tip遇到乱码,按这个顺序查:连接字符集 -> 库字符集 -> 表字符集 -> 列字符集。只要有一环不对,就可能乱码。统一成 utf8mb4 基本能根治。
11.8 转换字符集:CONVERT 和 CAST
已经存了 latin1 的数据要转成 utf8mb4,用 CONVERT 或 CAST 函数:
-- 转换字符串的字符集
SELECT CONVERT('abc' USING utf8mb4);
-- 用 CAST
SELECT CAST('abc' AS CHAR CHARACTER SET utf8mb4);
把整张表的列转字符集:
-- 把 name 列转成 utf8mb4
ALTER TABLE users MODIFY name VARCHAR(50) CHARACTER SET utf8mb4;
Warning字符集转换要小心”丢字符”:如果原数据里有目标字符集表示不了的字符,转换会变成问号
?。一般 latin1 -> utf8mb4 安全,反过来不一定。转换前务必备份。
11.9 一个完整的”中文不乱码”配置
把这一节学到的串起来,给一个生产可用的配置:
my.cnf / my.ini:
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_0900_ai_ci
建库建表:
CREATE DATABASE shop
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
USE shop;
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
nickname VARCHAR(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
连接时:
SET NAMES utf8mb4;
这样从服务器到库到表到连接,全链路 utf8mb4,中文、emoji 都不会再乱码。
11.10 小结
这一节理清了字符集和排序规则:
- 字符集决定能存什么字符、占多少字节;
- MySQL 的
utf8是 3 字节残废版,统一用utf8mb4; - 8.0+/26.7 默认字符集
utf8mb4,默认排序规则utf8mb4_0900_ai_ci; - 排序规则后缀
_ci不区分大小写、_cs区分、_bin二进制; - 字符集四级:服务器 < 数据库 < 表 < 列,列优先级最高;
SET NAMES utf8mb4统一连接字符集,根治乱码;- 转换字符集用
CONVERT/CAST,转换前要备份。
数据类型和字符集都讲完了,下一节终于开始建表了。