MySQL 排序规则(Collation)详解

MySQL 排序规则(Collation)详解

本文系统讲解 MySQL 8.0 中 字符集(Character Set)与排序规则(Collation) 的概念、命名、层级继承、比较语义及对查询/索引/唯一约束的影响。
默认以 MySQL 8.0 + utf8mb4 为基准;涉及 5.7 迁移处会单独标注。
官方参考:MySQL 8.0 — Character Sets and Collations

建议阅读顺序:先看 一、总览二、排序规则命名解读,再查阅 五、比较与排序语义八、对索引与约束的影响;升级场景可结合 01-MySQL-5.7到8.0差异详解 第四节。

相关文档03-MySQL各类索引详解(索引键比较依赖排序规则)、02-MySQL增删改查执行过程(filesort 与 ORDER BY)、01-MySQL-5.7到8.0差异详解(8.0 默认 collation 变更)。


一、总览

1.1 字符集与排序规则的关系

在 MySQL 中,字符集 定义「能存哪些字符、每个字符如何编码」;排序规则(Collation) 定义「这些字符如何 比较排序」。

┌─────────────────────────────────────────────────────────────────┐
│                    字符集 + 排序规则                              │
├──────────────────────┬──────────────────────────────────────────┤
│  Character Set       │  Collation                                 │
│  (字符集)            │  (排序规则)                               │
├──────────────────────┼──────────────────────────────────────────┤
│  编码:utf8mb4 占 1~4 字节 │  比较:'a' 与 'A' 是否相等                 │
│  能否存 emoji、生僻字    │  排序:'ä' 排在 'z' 前还是后                 │
│  校验非法字节序列        │  是否区分重音、是否区分大小写                   │
└──────────────────────┴──────────────────────────────────────────┘
         │                              │
         └──────── 每个 Collation 必须属于且仅属于一个 Character Set
概念 作用 典型配置
Character Set 存储与传输时的字节编码 utf8mb4
Collation =, <, >, ORDER BY, GROUP BY, DISTINCT 的比较规则 utf8mb4_0900_ai_ci
Charset + Collation 成对出现 列、表、库、连接各层都可指定 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci

关键结论:排序规则不是「可选装饰」,它直接决定 相等性判断——两条看起来不同的字符串,在某种 collation 下可能被视为 同一条记录(影响唯一索引、主键冲突、JOIN 匹配)。

1.2 为什么排序规则值得单独学习

场景 排序规则的影响
WHERE name = '张三' 查不到 连接 collation 与列 collation 不一致,或列用了 binary
唯一索引报 Duplicate entry,肉眼却不同 ci 规则下 Aa 视为相同
ORDER BY 结果与预期字母序不符 utf8mb4_general_ciutf8mb4_unicode_ci 排序权重不同
跨表 JOIN 报 Illegal mix of collations 两列 collation 不可强制转换
5.7 升 8.0 后应用排序/去重行为变化 默认从 utf8mb4_general_ci 等切到 utf8mb4_0900_ai_ci
索引无法用于 ORDER BY 表达式或 COLLATE 导致与索引键 collation 不一致

1.3 层级与继承

MySQL 在多个层级维护「当前字符集 / 排序规则」,内层覆盖外层

                    ┌─────────────────┐
                    │  Server 默认     │  character_set_server / collation_server
                    └────────┬────────┘
                             ▼
                    ┌─────────────────┐
                    │  Database       │  DEFAULT CHARACTER SET / COLLATE
                    └────────┬────────┘
                             ▼
                    ┌─────────────────┐
                    │  Table          │  DEFAULT CHARSET / COLLATE(列默认)
                    └────────┬────────┘
                             ▼
                    ┌─────────────────┐
                    │  Column         │  CHARACTER SET ... COLLATE ...(最终存储)
                    └────────┬────────┘
                             ▼
              ┌──────────────┴──────────────┐
              ▼                             ▼
     ┌─────────────────┐           ┌─────────────────┐
     │  Connection     │           │  Expression     │
     │  会话字符集      │           │  COLLATE 子句    │
     └─────────────────┘           └─────────────────┘
层级 查看方式 说明
Server SHOW VARIABLES LIKE 'collation%'; 8.0 默认 utf8mb4_0900_ai_ci
Database SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA 建库未指定则继承 Server
Table SHOW TABLE STATUS / information_schema.TABLES 建表未指定则继承 Database
Column SHOW FULL COLUMNS FROM t 实际比较以列 collation 为准
Connection SHOW VARIABLES LIKE 'collation_%'; 影响字面量、未限定列的临时结果
Expression expr COLLATE utf8mb4_bin 单次运算强制规则
-- 查看各层配置
SHOW VARIABLES WHERE Variable_name IN (
  'character_set_server', 'collation_server',
  'character_set_database', 'collation_database',
  'character_set_connection', 'collation_connection'
);

SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'mydb';

SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'mydb' AND TABLE_NAME = 'users';

二、排序规则命名解读

MySQL 的 collation 名称通常形如:utf8mb4_0900_ai_ci

utf8mb4  _  0900  _  ai  _  ci
   │         │        │     │
   │         │        │     └── 是否区分大小写(case)
   │         │        └──────── 是否区分重音(accent)
   │         └───────────────── Unicode 版本 / 算法代际
   └─────────────────────────── 所属字符集

2.1 常见后缀含义

后缀 / 片段 全称 含义 示例
_ci case-insensitive 不区分大小写 'A' = 'a'
_cs case-sensitive 区分大小写 'A' ≠ 'a'
_bin binary 字节值 比较,最快、最「严格」 'A' ≠ 'a'(0x41 ≠ 0x61)
_ai accent-insensitive 不区分重音 'é' = 'e'(在支持该语义的规则下)
_as accent-sensitive 区分重音 'é' ≠ 'e'
_0900 Unicode 9.0 基于 UCA 9.0 的排序算法(8.0 主力) utf8mb4_0900_ai_ci
_unicode UCA 较早的 Unicode 排序实现 utf8mb4_unicode_ci
_general 简化算法 较快但语义较粗 utf8mb4_general_ci

注意:并非每个 collation 名都显式包含 ai/as;老规则如 utf8mb4_general_ci 隐含了「不区分大小写」等行为,需查官方说明或 SHOW COLLATIONFlag 列。

2.2 utf8mb4 常用排序规则对比

Collation 大小写 重音 排序质量 性能 典型场景
utf8mb4_0900_ai_ci 不区分 不区分 (UCA 9.0) 8.0 默认,新项目推荐
utf8mb4_unicode_ci 不区分 部分语言更准 中等 5.7 时代常见,多语言排序
utf8mb4_general_ci 不区分 较快 5.7 默认之一,升级需评估
utf8mb4_bin 按字节 按字节 N/A 最快 区分大小写、固定编码比较、路由键
utf8mb4_0900_as_cs 区分 区分 较好 需要严格区分大小写与重音
-- 查看某字符集下所有可用排序规则
SHOW COLLATION LIKE 'utf8mb4%';

-- 查看某排序规则的所属字符集与是否默认
SHOW COLLATION WHERE Collation = 'utf8mb4_0900_ai_ci';

2.3 utf8 与 utf8mb4

名称 实际含义 最大字节 状态
utf8 utf8mb3 别名 3 8.0 中 已废弃,勿用于新表
utf8mb4 完整 UTF-8 4 推荐,支持 emoji 与全部 Unicode

排序规则名中的 utf8mb4 前缀表示该规则 只能用于 utf8mb4 字符集的列latin1_swedish_ci 只能用于 latin1 列。跨字符集比较需显式 CONVERT(... USING ...) 且可能丢失字符。


三、配置与 DDL 实践

3.1 服务器与连接

# my.cnf / my.ini — 推荐生产配置
[mysqld]
character_set_server=utf8mb4
collation_server=utf8mb4_0900_ai_ci

[mysql]
default-character-set=utf8mb4
-- 会话级(影响当前连接字面量与未指定 collation 的运算)
SET NAMES utf8mb4 COLLATE utf8mb4_0900_ai_ci;
-- 等价于:
SET character_set_client = utf8mb4;
SET character_set_connection = utf8mb4;
SET character_set_results = utf8mb4;
SET collation_connection = utf8mb4_0900_ai_ci;
变量 作用
character_set_client 客户端发来的字节如何解释
character_set_connection 字面量、无列参与的字符串运算
collation_connection 连接上字符串比较的默认规则
character_set_results 返回给客户端的编码

3.2 建库、建表、改列

-- 数据库
CREATE DATABASE app
  DEFAULT CHARACTER SET utf8mb4
  DEFAULT COLLATE utf8mb4_0900_ai_ci;

-- 表级默认(列未写 COLLATE 时继承)
CREATE TABLE users (
  id    BIGINT PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  code  VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
  UNIQUE KEY uk_email (email)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 修改已有列(大表会锁表/在线 DDL,需评估)
ALTER TABLE users
  MODIFY email VARCHAR(255)
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_0900_ai_ci
    NOT NULL;

3.3 COLLATE 子句

在 SQL 中可 单次 指定比较规则,不改变列定义:

-- WHERE:强制区分大小写比较
SELECT * FROM users WHERE email COLLATE utf8mb4_bin = 'User@Example.com';

-- ORDER BY:按二进制序排序
SELECT name FROM users ORDER BY name COLLATE utf8mb4_bin;

-- JOIN:统一两侧规则(示例)
SELECT *
FROM orders o
JOIN customers c
  ON o.customer_code COLLATE utf8mb4_0900_ai_ci = c.code;

原则:能用列级统一 collation 解决的,尽量避免在 SQL 里到处写 COLLATE——否则优化器可能 无法使用索引(见第八节)。


四、比较规则与强制转换(Coercion)

4.1 比较时如何选 collation

当两个字符串运算(=, <, JOIN, UNION, DISTINCT 等)碰撞时,MySQL 按 coercion 规则 选择「主导 collation」:

列的 collation(非 binary)
        │
        ▼ 优先
┌───────────────────┐     混合且不可调和     ┌────────────────────┐
│ 两侧同为某 _ci     │ ──────────────────▶ │ Error 1267            │
│ 或显式 COLLATE     │                     │ Illegal mix of ...    │
└───────────────────┘                     └────────────────────┘
        │
        ▼
连接 collation_connection(若参与的是字面量)
情况 结果
列 vs 同列 collation 的字面量 的 collation
列 vs 不同 collation 的列 若一方可 隐式提升 则转换;否则报错
utf8mb4_general_ci vs utf8mb4_unicode_ci 可能隐式转换,但 应主动统一设计
任意 vs _bin _bin_ci 混用常触发错误或全表扫描
-- 错误示例:混用 collation
SELECT * FROM t1 a
JOIN t2 b ON a.name = b.name;
-- ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT)
-- and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='

4.2 诊断 collation 冲突

-- 查看参与运算的对象各自 collation
SELECT
  a.COLUMN_NAME,
  a.COLLATION_NAME AS coll_a,
  b.COLLATION_NAME AS coll_b
FROM information_schema.COLUMNS a
JOIN information_schema.COLUMNS b
  ON a.TABLE_SCHEMA = b.TABLE_SCHEMA
WHERE a.TABLE_SCHEMA = 'mydb'
  AND a.TABLE_NAME = 't1'
  AND b.TABLE_NAME = 't2'
  AND a.COLUMN_NAME = 'name';

修复路径(择一):

  1. 改列定义 使相关列 collation 一致(根治)。
  2. 在 JOIN/WHERE 中对一侧 COLLATE(临时,可能影响索引)。
  3. 对一侧 CONVERT(col USING utf8mb4) 再比较(慎用,易失索引)。

五、比较与排序语义

5.1 相等性(= 与 DISTINCT / GROUP BY)

-- utf8mb4_0900_ai_ci:不区分大小写
SELECT 'ABC' = 'abc';   -- 1(真)

-- utf8mb4_bin:按字节
SELECT 'ABC' = 'abc';   -- 0(假)

-- 德语 ß 在部分规则下与 ss 等价(0900 系列更贴近 Unicode 标准)
-- 实际结果依赖具体 collation,升级后需用业务数据回归
操作 使用的规则
WHERE col = ? 列 collation
GROUP BY col 列 collation 决定「同一组」
DISTINCT col 列 collation 决定「重复」
UNIQUE 索引 列 collation 决定冲突

5.2 ORDER BY 排序

ORDER BY排序规则定义的权重 排列,而非简单的 Unicode 码点序(_bin 除外)。

CREATE TABLE demo (
  name VARCHAR(32) COLLATE utf8mb4_unicode_ci
);
INSERT INTO demo VALUES ('apple'), ('Äpfel'), ('banana'), ('Zebra');

SELECT name FROM demo ORDER BY name;
-- 顺序与 utf8mb4_bin、utf8mb4_general_ci 可能不同

SELECT name FROM demo ORDER BY name COLLATE utf8mb4_bin;
-- 按 UTF-8 字节序
现象 原因
大写 Z 排在小写 a 前/后不符合「字典序」直觉 _ci 规则先比「字母身份」再比大小写
升级后列表顺序变化 general_ci0900_ai_ci 权重表不同
ORDER BY 无法走索引 ORDER BY col COLLATE xxx 与索引键 collation 不一致 → filesort

03-MySQL各类索引详解 中「降序索引 / filesort」结合:排序规则不一致时,即使列上有索引,也可能额外排序。

5.3 LIKE 与前缀匹配

LIKE_ci 规则下 不区分大小写(除非用 _bin):

SELECT 'Hello' LIKE 'hello';  -- 在 _ci 下为 1

-- 前缀索引长度与 collation 相关:相同 VARCHAR(100) 在不同 collation 下
-- 排序与去重行为不同,前缀索引「区分度」也会变化

通配符 %_ 的比较同样受 collation 约束;utf8mb4_0900_ai_ci 对 Unicode 特殊字符的处理比 general_ci 更规范。

5.4 长度与 pad 属性(8.0 重要变更)

MySQL 8.0 中 utf8mb4_0900_* 等规则默认 PAD SPACECHAR(n) 比较时 尾部空格 可能参与语义(与 NO PAD 规则不同)。

属性 行为
PAD SPACE CHAR 尾部空格在比较时可能被忽略或填充
NO PAD 尾部空格参与比较,更严格

升级后若业务依赖「CHAR 尾部空格等价」,需用真实数据验证。查看方式:

SELECT COLLATION_NAME, PAD_ATTRIBUTE
FROM information_schema.COLLATIONS
WHERE COLLATION_NAME LIKE 'utf8mb4%'
ORDER BY COLLATION_NAME;

六、常见排序规则深度对比

6.1 general_ci vs unicode_ci vs 0900_ai_ci

比较维度          general_ci        unicode_ci         0900_ai_ci
─────────────────────────────────────────────────────────────────
算法复杂度         低                 中                  中高
多语言正确性       一般               较好                好(UCA 9.0)
性能              较快               中等                好(8.0 优化)
8.0 默认           否                 否                  是
升级风险           与 0900 结果可能不同  与 0900 可能不同     新项目基准

实践建议

  • 新项目:统一 utf8mb4 + utf8mb4_0900_ai_ci
  • 从 5.7 迁移:不要假设「都是 utf8mb4 就没问题」——collation 不同仍会导致排序、唯一约束、应用缓存键变化
  • 需要大小写敏感的唯一约束(如用户名):列级使用 utf8mb4_binutf8mb4_0900_as_cs,不要用默认 _ci

6.2 何时使用 utf8mb4_bin

适合 _bin 不适合 _bin
API Key、邀请码、区分大小写的登录名 需要「人类可读」字典序的展示列表
与外部系统按字节一致的对照 需要不区分大小写的邮箱登录
纯 ASCII 且要最省 CPU 的比较 需要正确的中文/多语言排序
-- 邮箱:通常不区分大小写 → _ci
email VARCHAR(255) COLLATE utf8mb4_0900_ai_ci

-- 外部系统 token:区分每一个字符 → _bin
token VARCHAR(64) COLLATE utf8mb4_bin

七、迁移与版本差异(5.7 → 8.0)

7.1 默认变更速查

层级 5.7 常见默认 8.0 默认
Server charset latin1 utf8mb4
Server collation latin1_swedish_ci utf8mb4_0900_ai_ci
新建库(未指定) 继承 Server 继承 Server
已有库表 升级 不自动改 保持原 collation

详见 01-MySQL-5.7到8.0差异详解 第四节。

7.2 升级检查清单

-- 找出仍使用旧 collation 的对象
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_COLLATION NOT LIKE 'utf8mb4_0900%'
  AND TABLE_SCHEMA NOT IN ('mysql', 'sys', 'performance_schema', 'information_schema');

SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE COLLATION_NAME IS NOT NULL
  AND COLLATION_NAME NOT LIKE 'utf8mb4_0900%'
  AND TABLE_SCHEMA = 'app';
检查项 说明
唯一索引是否允许多组「ci 意义下相同」数据 _ci_bin 可能 暴露隐藏重复
应用 ORDER BY 结果是否作为业务逻辑 排序权重变化会导致分页、游标错位
主从复制字符集 8.0 从库默认 utf8mb4,与 5.7 主库混用时注意连接与表定义
注释 / 元数据非法字符 8.0 校验更严,含非法字符的表注释可能阻升级

7.3 修改 collation 的操作注意

-- 修改表默认 + 各字符列(示例,生产需 pt-osc 或在线 DDL 评估)
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
风险 说明
全表重建 CONVERT TO 常重写表,大表耗时长、占磁盘
隐式截断 非法字符或编码转换失败会报错或截断(严格模式下报错)
索引长度 utf8mb4 每字符最多 4 字节,索引键长度上限 3072 字节(innodb_large_prefix
重复键 _ci 下视为不同的两行,转 _bin 后可能冲突

八、对索引与约束的影响

8.1 唯一索引与主键

唯一性在 列 collation 语义下 判断:

CREATE TABLE t (
  name VARCHAR(64) COLLATE utf8mb4_0900_ai_ci,
  UNIQUE KEY uk_name (name)
);

INSERT INTO t VALUES ('Test');
INSERT INTO t VALUES ('test');  -- Duplicate entry(ci 下等价)

若业务要求 Testtest 共存,必须改用 utf8mb4_binutf8mb4_0900_as_cs区分大小写 的规则。

8.2 索引能否用于排序与查找

优化器使用索引的前提之一是:比较运算的 collation 与索引键一致

-- 索引:name 列为 utf8mb4_0900_ai_ci
SELECT * FROM users WHERE name = 'alice';           -- 可用索引
SELECT * FROM users WHERE name COLLATE utf8mb4_bin = 'alice';  -- 往往不能用索引
SELECT * FROM users ORDER BY name;                  -- 可用索引排序
SELECT * FROM users ORDER BY name COLLATE utf8mb4_bin;         -- 可能 filesort
EXPLAIN 线索 含义
Using filesort 排序规则或方向与索引不匹配的可能原因之一
type: ALL WHERE 中 COLLATE 导致无法走索引

8.3 联合索引与字符串列

联合索引 (status, email) 中,若 email_ci,则 WHERE email = ? 不区分大小写;前缀列 的比较规则同样影响最左前缀能否使用。字符串列与数值列混用时,还要注意隐式类型转换(与 collation 无关但常同时出现)。


九、性能与实现要点

9.1 比较成本

Collation 类型 相对 CPU 成本 说明
_bin 最低 memcmp 式字节比较
_general_ci 较低 简化权重表
_unicode_ci / _0900_* 中等 完整 UCA 权重与收缩规则

高 QPS 的点查若 不需要 _ci 语义,列级用 _bin 可略降 CPU(收益通常小于网络与 IO,除非极端热点)。

9.2 排序与临时表

大结果集 ORDER BYDISTINCT 在 collation 复杂时,可能:

  • 使用 filesort(内存或磁盘)
  • 使用 临时表 去重(DISTINCT / GROUP BY

02-MySQL增删改查执行过程 中优化器阶段对照:字符集层发生在 Server 比较器,但代价体现在执行器排序与临时表。

9.3 连接池与 ORM

问题 建议
连接未设 utf8mb4 连接池初始化执行 SET NAMES utf8mb4 COLLATE utf8mb4_0900_ai_ci
ORM 建表与 DBA 手工建表 collation 不一致 在 migration 中显式写 COLLATE
Java utf8 vs MySQL utf8mb4 JDBC URL 指定 characterEncoding=UTF-8,表用 utf8mb4

十、实践设计指南

10.1 推荐约定(8.0 生产)

┌────────────────────────────────────────────────────────────┐
│  Server / Database / Table 默认:utf8mb4 + utf8mb4_0900_ai_ci │
├────────────────────────────────────────────────────────────┤
│  用户可见文本、邮箱、姓名、标题:继承默认 _ci                  │
│  大小写敏感标识符、Token、Hash 前缀:列级 utf8mb4_bin          │
│  需要严格语言排序的报表:单独评估 _unicode_ci / _0900_as_cs    │
│  SQL 中避免对索引列滥用 COLLATE                              │
└────────────────────────────────────────────────────────────┘

10.2 反模式

反模式 后果
同一张表内同类字段 collation 不一致 JOIN 报错或全表扫描
依赖 general_ci 的「偶然」排序做分页 升级 0900 后顺序变化
唯一索引用 _ci 存用户名 Adminadmin 冲突
混用 utf8(mb3)与 utf8mb4 emoji 截断、转换错误
在 WHERE 中对列包函数 + COLLATE 索引失效

10.3 排查命令速查

-- 当前会话
SHOW VARIABLES LIKE 'collation%';
SHOW VARIABLES LIKE 'character_set%';

-- 对象级
SHOW FULL COLUMNS FROM mydb.users;
SHOW CREATE TABLE mydb.users;

-- 字面量在当前连接下的 collation
SELECT COLLATION('abc');

-- 两字符串是否在当前规则下相等
SELECT 'A' = 'a' COLLATE utf8mb4_0900_ai_ci;

十一、小结

要点 一句话
定义 Collation 决定比较与排序,与 Character Set 成对、不可混用
命名 _ci / _bin / _ai / _0900 等后缀表达大小写、重音与算法代际
层级 Server → Database → Table → Column;列级为准
查询 影响 =, ORDER BY, GROUP BY, LIKE, JOIN
约束 唯一索引在 collation 语义下判重,ci 下大小写等价
索引 WHERE/ORDER BY 的 collation 须与索引键一致,否则易 filesort 或全表扫描
8.0 默认 utf8mb4_0900_ai_ci;升级旧库不会自动转换
实践 全局统一默认,敏感标识用 _bin;少在 SQL 里临时 COLLATE

排序规则是 数据语义 的一部分:选错 collation 不会立刻报错,但会在 唯一约束、排序分页、跨表关联 上缓慢暴露问题。新建系统应一次性定好 charset/collation 策略;遗留系统升级则以 列级清单 + 业务回归 为主,避免仅改 Server 默认值却以为「已经 utf8mb4 了」。


参考链接

posted @ 2026-06-26 15:51  一个老码农  阅读(29)  评论(0)    收藏  举报